Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
blpapi / correlationid.py
Size: Mime:
from ctypes import (
    byref,
    c_uint16,
    c_void_p,
    sizeof,
)
from . import internals
from . import pycbhelpers
from .ctypesutils import safePOD, py_objectFromVoid
from typing import Any


class CorrelationId:
    r"""
    A key used to identify individual subscriptions or requests.

    Two :class:`CorrelationId` objects are considered equal if they have the same
    :meth:`type()` and:

    - Hold the same (not just equal!) objects, if the type is :attr:`OBJECT_TYPE`.
    - Hold equal integers, if the type is :attr:`INT_TYPE` or :attr:`AUTOGEN_TYPE`.

    If the type is :attr:`UNSET_TYPE`, then the two :class:`CorrelationId` objects
    are always equal (as the value of both will necessarily be ``None``).

    It is possible that a user constructed :class:`CorrelationId` and a
    :class:`CorrelationId` generated by the API could return the same result for
    :meth:`value()`. However, they will not compare equal because they have a
    different :meth:`type()`.

    :class:`CorrelationId` objects are passed to many of the :class:`Session`
    object methods which initiate asynchronous operations and are obtained from
    :class:`Message` objects which are delivered as a result of those asynchronous
    operations.

    When subscribing or requesting information, an application has the choice of
    providing a :class:`CorrelationId` they construct themselves or allowing the
    session to construct one for them. If the application supplies a
    :class:`CorrelationId`, it must not re-use the value contained in it in another
    :class:`CorrelationId`, whilst the original request or subscription is still
    active.

    The ``xxx_TYPE`` class attributes represent the possible types of
    :class:`CorrelationId`.
    """

    UNSET_TYPE = internals.CORRELATION_TYPE_UNSET
    """The :class:`CorrelationId` is unset. That is, it was created by the
    default :class:`CorrelationId` constructor."""

    INT_TYPE = internals.CORRELATION_TYPE_INT
    """The :class:`CorrelationId` was created from an :class:`int` supplied by the user."""

    OBJECT_TYPE = internals.CORRELATION_TYPE_POINTER
    """The :class:`CorrelationId` was created from an object supplied by
    the user."""

    AUTOGEN_TYPE = internals.CORRELATION_TYPE_AUTOGEN
    """The :class:`CorrelationId` was created internally by API."""

    MAX_CLASS_ID = internals.CORRELATION_MAX_CLASS_ID
    """The maximum value allowed for ``classId``."""

    STRUCT_SZ = sizeof(internals.CidStruct)

    __TYPE_NAMES = {
        UNSET_TYPE: "UNSET",
        INT_TYPE: "INTEGER",
        OBJECT_TYPE: "OBJECT",
        AUTOGEN_TYPE: "AUTOGEN",
    }

    @staticmethod
    def cidFromCPtr(ptr: int) -> "CorrelationId":
        # used in _subscriptionPreprocessProxy
        cid = internals.CidStruct.from_buffer_copy(
            internals.CidStruct.from_address(ptr)
        )
        return CorrelationId(cid)

    def __init__(self, *argv: Any, **_kwargs: Any) -> None:
        r"""
        ``CorrelationId([value[, classId=0]])`` constructs a :class:`CorrelationId`
        object.
        If ``value`` is an :class:`int` then the
        created :class:`CorrelationId` will have type :attr:`INT_TYPE`. Otherwise, it
        will have type :attr:`OBJECT_TYPE`.
        If no arguments are specified, then the type will be :attr:`UNSET_TYPE`.
        The maximum allowed ``classId`` value is :attr:`MAX_CLASS_ID`.
        """
        argc = len(argv)
        assert argc >= 0 and argc <= 2
        if argc == 0:  # this is empty/default
            self.flags = internals.CidFlags(
                CorrelationId.STRUCT_SZ, CorrelationId.UNSET_TYPE, 0, 0
            )
            self.rawvalue = internals.CidValue(0)
        elif isinstance(argv[0], int):  # this is int
            classid = (
                safePOD(argv[1], c_uint16).value
                if argc > 1 and isinstance(argv[1], int)
                else 0
            )
            self.flags = internals.CidFlags(
                CorrelationId.STRUCT_SZ, CorrelationId.INT_TYPE, classid, 0
            )
            self.rawvalue = internals.CidValue(intValue=argv[0])
        elif isinstance(argv[0], internals.CidStruct):  # this came from C
            self.flags = argv[0].flags
            if self.flags.valueType != CorrelationId.OBJECT_TYPE:
                # this is an in-place value int/autogen, just store it
                self.rawvalue = argv[0].rawvalue
            else:
                # this is an object, we need to handle lifetime
                if (
                    internals.is_known_obj(byref(argv[0].rawvalue.ptrValue))
                    == 0
                ):
                    # must be recap
                    self.rawvalue = argv[0].rawvalue
                    # we must become co-owners and bump using C++ manager
                    pv = self.rawvalue.ptrValue
                    pv.manager(
                        byref(pv),
                        byref(argv[0].rawvalue.ptrValue),
                        internals.ManagedPtr.COPY,
                    )
                else:  # it is a proper py-object
                    pyobj = CorrelationId._objectFromStruct(argv[0])
                    self.rawvalue = internals.cidValueForObj(pyobj)
                    # do not copy user data from the original struct received
                    # from C++, because it is valid only within the C++ scope
                    # and can be invalidated there while it is held here
                    assert argv[0].rawvalue.ptrValue.userData[0].ptr
                    assert not self.rawvalue.ptrValue.userData[0].ptr
        else:  # this is a py-object, including None
            classid = (
                safePOD(argv[1], c_uint16).value
                if argc > 1 and isinstance(argv[1], int)
                else 0
            )

            self.flags = internals.CidFlags(
                CorrelationId.STRUCT_SZ,
                CorrelationId.OBJECT_TYPE,
                classid,
                internals.CORRELATION_INTERNAL_CLASS_FOREIGN_OBJECT,
            )
            self.rawvalue = internals.cidValueForObj(argv[0])

        self.thestruct = internals.CidStruct(
            flags=self.flags, rawvalue=self.rawvalue
        )

    def __del__(self) -> None:
        if self.type() != CorrelationId.OBJECT_TYPE:
            return

        # this is safe for py-object, recaps, and even None
        pv = self.rawvalue.ptrValue
        pv.manager(byref(pv), None, internals.ManagedPtr.DESTROY)  # dec ref.

    def type(self) -> int:
        r"""
        Returns:
            int: The type of this CorrelationId object (see the ``xxx_TYPE`` class
            attributes)
        """
        return self.thestruct.flags.valueType

    def classId(self) -> int:
        r"""
        Returns:
            int: The user defined classification of this :class:`CorrelationId`
            object
        """
        return self.thestruct.flags.classId

    def _internalClassId(self) -> int:
        return self.thestruct.flags.internalClassId

    def _asInt(self) -> int:
        return self.thestruct.rawvalue.intValue

    def value(self) -> Any:
        """
        Returns:
            int or long or object: The value of this CorrelationId object.

        The return value depends on this :class:`CorrelationId`'s value
        type and could be:

        - Integer (``type() == CorrelationId.INT_TYPE``
          or ``type() == CorrelationId.AUTOGEN_TYPE``)
        - Object (``type() == CorrelationId.OBJECT_TYPE``)
        - ``None`` (``type() == CorrelationId.UNSET_TYPE``)
        """
        valueType = self.type()
        if (
            valueType == CorrelationId.INT_TYPE
            or valueType == CorrelationId.AUTOGEN_TYPE
        ):
            return self._asInt()
        elif valueType == CorrelationId.OBJECT_TYPE:
            return self._asObject()
        else:
            return None

    @staticmethod
    def _objectFromStruct(cid: internals.CidStruct) -> Any:
        if cid.flags.valueType == CorrelationId.OBJECT_TYPE:
            if internals.is_known_obj(byref(cid.rawvalue.ptrValue)) == 0:
                # must be recaps then
                return None

            pyobj = py_objectFromVoid(c_void_p(cid.rawvalue.ptrValue.pointer))
            # py_object is a thin wrapper, it does not bump the ref.count
            # but this is fine, the receiver of this will
            return pyobj.value
        return None

    def _asObject(self) -> Any:
        return CorrelationId._objectFromStruct(self.thestruct)

    def __repr__(self) -> str:
        if self.type() == CorrelationId.OBJECT_TYPE:
            return f"CorrelationId(flags={self.thestruct.flags}, rawvalue.ptrValue=object)"
        return f"CorrelationId(flags={self.thestruct.flags}, rawvalue.intValue={self._asInt()})"

    def __str__(self) -> str:
        """x.__str__() <==> str(x)"""
        valueType = self.type()
        valueTypeName = CorrelationId.__TYPE_NAMES[valueType]

        if valueType == CorrelationId.UNSET_TYPE:
            return valueTypeName
        else:
            return (
                f"({valueTypeName}: {self.value()!r}, "
                f"ClassId: {self.classId()}, "
                f"InternalClassId: {self._internalClassId()})"
            )

    def __hash__(self) -> int:
        if self.type() == CorrelationId.OBJECT_TYPE:
            return hash(
                (
                    self.type(),
                    self.classId(),
                    self._internalClassId(),
                    self.thestruct.rawvalue.ptrValue.pointer,
                )
            )
        return hash(
            (
                self.type(),
                self.classId(),
                self._internalClassId(),
                self._asInt(),
            )
        )

    def __eq__(self, other: Any) -> bool:
        """x.__eq__(y) <==> x==y"""
        try:
            if self is other:
                return True
            if other is None:
                return False
            if self.type() != other.type():
                return False
            if self.classId() != other.classId():
                return False
            if self._internalClassId() != other._internalClassId():
                return False
            if self.type() == CorrelationId.OBJECT_TYPE:
                # we care about it being SAME object, not just equal/equivalent
                return self._asObject() is other._asObject()
            return self._asInt() == other._asInt()
        except Exception:  # pylint: disable=broad-exception-caught
            return NotImplemented

    def __ne__(self, other: Any) -> bool:
        """x.__ne__(y) <==> x!=y"""
        equal = self.__eq__(other)
        return NotImplemented if equal is NotImplemented else not equal


pycbhelpers.correlationIdWrapper = CorrelationId.cidFromCPtr