Create python XInterface implementation?

I would like to create a python class that implements XInterface, which I indend to use in OOO Development Tools.

The Developers Guide talks about requriements but it not clear if this can be done in python.

How would I implement queryInterface(), acquire(), and release()

In short I want to make a python class that implements XIndexAccess. This class wil be able to take in a list or a tuple. Then I can pass an instance of the class to exsiting code that expects an XIndexAccess as input.

Why not derive from unohelper.Base, as explained in https://www.openoffice.org/udk/python/python-bridge.html ?

I did.
Here is what I have so far.

from __future__ import annotations
from typing import Any, Tuple, TYPE_CHECKING

# from ooo.dyn.uno.type_class import TypeClass
import uno
from com.sun.star.container import XIndexAccess
import unohelper

if TYPE_CHECKING:
    from com.sun.star.beans import PropertyValue


class IndexAccess(unohelper.Base, XIndexAccess):
    def __init__(self, data: Tuple[Tuple[PropertyValue, ...]]):
        super().__init__()
        self._data = data

    def getCount(self):
        return len(self._data)

    def getByIndex(self, index):
        return self._data[index]

    def hasElements(self):
        return self.getCount() > 0

    def getElementType(self) -> Any:
        """
        Gets te Element Type
        """
        t = uno.Type("[]com.sun.star.beans.PropertyValue", "SEQUENCE")
        return t

The I am testing as follows but assert ia.getElementType() is totally crashing office (Calc) at this point.

data = (Props.make_props(A=1, B=2), Props.make_props(C=3, D=4))
ia = IndexAccess(data)
assert ia.getElementType()

That is not the complete code. What do I need to import to run?
Please don’t tell me I need your complete package. Something minimal, that only needs LibreOffice.

EDIT: this reproduces:

import test_code
data=tuple()
ia = test_code.IndexAccess(data)
assert ia.getElementType()

Here is a runable example.
Must have ooo-dev-tools installed if running from command line or IDE.

Note that other interfaces are also not supported such as XServiceInfo and XTypeProvider.
So as is inst.supportsService("com.sun.star.container.XIndexAccess") will not work.

from __future__ import annotations
from typing import Any, Tuple, TYPE_CHECKING

import uno
from com.sun.star.container import XIndexAccess
from com.sun.star.beans import PropertyValue
import unohelper

from ooodev.loader import Lo
from ooodev.calc import CalcDoc


class IndexAccess(unohelper.Base, XIndexAccess):
    def __init__(self, data: Tuple[Tuple[PropertyValue, ...]]):
        super().__init__()
        self._data = data

    def getCount(self):
        return len(self._data)

    def getByIndex(self, index):
        return self._data[index]

    def hasElements(self):
        return self.getCount() > 0

    def getElementType(self) -> Any:
        """
        Gets te Element Type
        """
        t = uno.Type("[]com.sun.star.beans.PropertyValue", "SEQUENCE")
        return t


def test_index_access():
    data = ((PropertyValue("A", 1), PropertyValue("B", 2)), (PropertyValue("C", 3), PropertyValue("D", 4)))
    ia = IndexAccess(data)
    assert ia.getElementType()


def main():
    loader = Lo.load_office(connector=Lo.ConnectPipe())
    doc = CalcDoc.create_doc(loader=loader, visible=True)
    try:
        test_index_access()
        Lo.delay(3_000)  # wait 3 seconds
    finally:
        doc.close()
        Lo.close_office()


if __name__ == "__main__":
    main()

Here is a screenshot from Vs Code of an XIndexAccess container.

I just tried:

def getElementType(self) -> Any:
    t = uno.Type("com.sun.star.beans.PropertyValue", "SEQUENCE")
    return t

Removed [] from uno.Type("com.sun.s....
Still totally crashes Office.

Ok, I got the type figured out. Needed to use uno.getTypeByName()

def getElementType(self) -> Any:
    t = uno.getTypeByName("[]com.sun.star.beans.PropertyValue")
    return t

image

1 Like

Indeed. unohelper.Base only implements XTypeProvider itself.

Ok I have a working solution.

Thanks @mikekaganski for you help.

from __future__ import annotations
from typing import Any, Tuple
import contextlib
import uno
from com.sun.star.container import XElementAccess
from com.sun.star.container import XIndexAccess
from com.sun.star.uno import XInterface
from com.sun.star.beans import PropertyValue
import unohelper

from ooodev.loader import Lo
from ooodev.calc import CalcDoc


class IndexAccess(unohelper.Base, XIndexAccess, XElementAccess, XInterface):
    __pyunointerface__: str = "com.sun.star.container.XIndexAccess"

    def __init__(self, data: Tuple[Tuple[PropertyValue, ...]]):
        super().__init__()
        self._data = data
        self._types = None

    # region XInterface
    def acquire(self) -> None:
        pass

    def release(self) -> None:
        pass

    def queryInterface(self, a_type: Any) -> Any:
        with contextlib.suppress(Exception):
            if a_type in self.getTypes():
                return self
        return None

    # end region XInterface

    # region XIndexAccess
    def getCount(self):
        return len(self._data)

    def getByIndex(self, index):
        return self._data[index]

    # endregion XIndexAccess

    # region XElementAccess
    def hasElements(self):
        return self.getCount() > 0

    def getElementType(self) -> Any:
        """
        Gets te Element Type
        """
        t = uno.getTypeByName("[]com.sun.star.beans.PropertyValue")
        return t

    # endregion XElementAccess

    # region XTypeProvider
    def getImplementationId(self) -> Any:
        """
        Obsolete unique identifier.
        """
        return b""

    def getTypes(self) -> Tuple[Any, ...]:
        """
        returns a sequence of all types (usually interface types) provided by the object.
        """
        if self._types is None:
            types = []
            types.append(uno.getTypeByName("com.sun.star.uno.XInterface"))
            types.append(uno.getTypeByName("com.sun.star.lang.XTypeProvider"))
            types.append(uno.getTypeByName("com.sun.star.container.XElementAccess"))
            types.append(uno.getTypeByName("com.sun.star.container.XIndexAccess"))
            self._types = tuple(types)
        return self._types


def test_index_access():
    data = ((PropertyValue("A", 1), PropertyValue("B", 2)), (PropertyValue("C", 3), PropertyValue("D", 4)))
    ia = IndexAccess(data)
    ia_type = uno.getTypeByName("com.sun.star.container.XIndexAccess")
    assert ia.queryInterface(ia_type) is ia
    assert ia.getCount() == 2
    assert ia.getByIndex(0) == data[0]


def main():
    loader = Lo.load_office(connector=Lo.ConnectPipe())
    doc = CalcDoc.create_doc(loader=loader, visible=True)
    try:
        test_index_access()
        Lo.delay(3_000)  # wait 3 seconds
    finally:
        doc.close()
        Lo.close_office()


if __name__ == "__main__":
    main()
1 Like

Why did you need to implement getTypes, getImplementationId, and the XInterface methods? others are understandable, but the whole idea of unohelper.Base is to have these already implemented for you?

In this case I have a IndexAccessComp class that implements my ElementAccessPartial class.

The ElementAccessPartial class does a validate check to ensure the component it was passed is a valid XElementAccess which makes a call to queryInterface() under the hood.

getTypes() supplies the type for queryInterface() in this implementation.

So, something like this:

from ooodev.adapter.container.index_access_comp import IndexAccessComp
# ...
data = ...
ia = IndexAccess(data)
iac = IndexAccessComp(ia)

my_cls = MyCls(iac)
myclass.do_work()
# ...

Do you say that it doesn’t work when you don’t manually provide these functions? As I said several times already, it is expected, designed, that these methods are already implemented for you, and you don’t have to do that yourself.

Ok I just did anothe little test.
It is not necessary to implement com.sun.star.lang.XTypeProvider methods getImplementationId() and getTypes() if class inherits from unohelper.Base. The solution example would still work.

so in the solution example I could implement XTypeProvider or unohelper.base both are not needed. Although my getTypes() is likely more efficient. XInterface is still needed to get access to queryInterface().

But why? Python has other means to test if object is XInterface, and that’s why the queryInterface isn’t exposed directly. You keep inventing wheels in most non-Pythonic way.

import uno
from com.sun.star.container import XIndexAccess

...

class IndexAccess(unohelper.Base, XIndexAccess):
    def __init__(self, data: Tuple[Tuple[PropertyValue, ...]]):
        super().__init__()
        self._data = data

    def getCount(self):
        return len(self._data)

    def getByIndex(self, index):
        return self._data[index]

    def hasElements(self):
        return self.getCount() > 0

    def getElementType(self) -> Any:
        return uno.getTypeByName("[]com.sun.star.beans.PropertyValue")


...

ia = IndexAccess(tuple())
isinstance(ia, XIndexAccess)
1 Like

By the way. supportsService is not expected to return True for interface names, only to service names. So your code is wrong anyway.

1 Like

That is correct. I made a mistake in that part.
Note that com.sun.star.lang.XServiceInfo is not implemented in the solution.