Repository URL to install this package:
|
Version:
3.26.1.1 ▾
|
blpapi
/
correlationid.py
|
|---|
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