Repository URL to install this package:
|
Version:
3.16.1 ▾
|
blpapi
/
abstractsession.py
|
|---|
# abstractsession.py
"""A common interface shared between publish and consumer sessions.
This file defines a class 'AbstractSession' - an interface which is shared
between its concrete implementations 'Session' and 'ProviderSession'.
SERVICE IDENTIFIER
------------------
A service identifier is the fully qualified service name which uniquely
identifies the service in the API infrastructure. A service must be of the
form "//<namespace>/<service-name>" where '<namespace>' and '<local-name>' are
non-empty strings of characters from the set '[-_.a-zA-Z0-9]'. Service
identifiers are case-insensitive, but clients are encouraged to prefer
identifiers without upper-case characters. Note that the <namespace> and
<service-name> cannot contain the character '/'.
"""
# pylint: disable=protected-access,useless-object-inheritance
from . import exception
from .exception import _ExceptionUtil
from .identity import Identity
from .service import Service
from . import internals
from .internals import CorrelationId
from . import utils
from .utils import get_handle
from .compat import with_metaclass
@with_metaclass(utils.MetaClassForClassesWithEnums)
class AbstractSession(object):
"""A common interface shared between publish and consumer sessions.
This class provides an abstract session which defines shared interface
between publish and consumer requests for Bloomberg.
Sessions manage access to services either by requests and responses or
subscriptions. A Session can dispatch events and replies in either a
synchronous or asynchronous mode. The mode of a Session is determined when
it is constructed and cannot be changed subsequently.
A Session is asynchronous if an ``eventHandler`` argument is supplied when
it is constructed. The ``nextEvent()`` method may not be called. All
incoming events are delivered to the ``eventHandler`` supplied on
construction.
If supplied, ``eventHandler`` must be a callable object that takes two
arguments: received :class:`Event` and related session.
A Session is synchronous if an ``eventHandler`` argument is not supplied
when it is constructed. The ``nextEvent()`` method must be called to read
incoming events.
Several methods in Session take a :class:`CorrelationId` parameter. The
application may choose to supply its own :class:`CorrelationId` values or
allow the Session to create values. If the application supplies its own
:class:`CorrelationId` values it must manage their lifetime such that the
same value is not reused for more than one operation at a time. The
lifetime of a :class:`CorrelationId` begins when it is supplied in a method
invoked on a Session and ends either when it is explicitly cancelled using
:meth:`cancel()` or ``unsubscribe()``, when a :attr:`~Event.RESPONSE`
:class:`Event` (not a :attr:`~Event.PARTIAL_RESPONSE`) containing it is
received or when a :attr:`~Event.SUBSCRIPTION_STATUS` :class:`Event` which
indicates that the subscription it refers to has been terminated is
received.
When using an asynchronous Session, the application must be aware that
because the callbacks are generated from another thread, they may be
processed before the call which generates them has returned. For example,
the :attr:`~Event.SESSION_STATUS` :class:`Event` generated by a
``startAsync()`` may be processed before ``startAsync()`` has returned
(even though ``startAsync()`` itself will not block).
This becomes more significant when Session generated
:class:`CorrelationId`\ s are in use. For example, if a call to
``subscribe()`` which returns a Session generated :class:`CorrelationId`
has not completed before the first :class:`Event`\ s which contain that
:class:`CorrelationId` arrive the application may not be able to interpret
those events correctly. For this reason, it is preferable to use user
generated :class:`CorrelationId`\ s when using asynchronous Sessions. This
issue does not arise when using a synchronous Session as long as the calls
to ``subscribe()`` etc are made on the same thread as the calls to
``nextEvent()``.
"""
def __init__(self, handle=None):
"""Instantiate an :class:`AbstractSession` with the specified handle.
Args:
handle: Handle to the underlying session
Raises:
NotImplementedError: If this class is instantiated directly
This function is for internal use only. Clients should create sessions
using one of the concrete subclasses of :class:`AbstractSession`.
"""
if self.__class__ is AbstractSession:
raise NotImplementedError("Don't instantiate this class directly.\
Create sessions using one of the concrete subclasses of this class.")
self.__handle = handle
def openService(self, serviceName):
"""Open the service identified by the specified ``serviceName``.
Args:
serviceName (str): Name of the service
Returns:
bool: ``True`` if the service is opened successfully, ``False``
otherwise.
Attempt to open the service identified by the specified ``serviceName``
and block until the service is either opened successfully or has failed
to be opened. Return ``True`` if the service is opened successfully and
``False`` if the service cannot be successfully opened.
The ``serviceName`` must contain a fully qualified service name. That
is, it must be of the form ``//<namespace>/<service-name>``.
Before :meth:`openService()` returns a :attr:`~Event.SERVICE_STATUS`
:class:`Event` is generated. If this is an asynchronous Session then
this :class:`Event` may be processed by the registered ``eventHandler``
before :meth:`openService()` has returned.
"""
return internals.blpapi_AbstractSession_openService(
self.__handle,
serviceName) == 0
def openServiceAsync(self, serviceName, correlationId=None):
"""Begin the process to open the service and return immediately.
Args:
serviceName (str): Name of the service
correlationId (CorrelationId): Correlation id to associate with
events generated as a result of this call
Returns:
CorrelationId: The correlation id used to identify the Events
generated as a result of this call
Begin the process to open the service identified by the specified
``serviceName`` and return immediately. The optional specified
``correlationId`` is used to track :class:`Event`\ s generated as a
result of this call.
The ``serviceName`` must contain a fully qualified service name. That
is, it must be of the form ``//<namespace>/<service-name>``.
The application must monitor events for a :attr:`~Event.SERVICE_STATUS`
:class:`Event` which will be generated once the service has been
successfully opened or the opening has failed.
"""
if correlationId is None:
correlationId = CorrelationId()
_ExceptionUtil.raiseOnError(
internals.blpapi_AbstractSession_openServiceAsync(
self.__handle,
serviceName,
get_handle(correlationId)))
return correlationId
def sendAuthorizationRequest(self,
request,
identity,
correlationId=None,
eventQueue=None):
"""Send the specified ``authorizationRequest``.
Args:
request (Request): Authorization request to send
identity (Identity): Identity to update with the results
correlationId (CorrelationId): Correlation id to associate with the
request
eventQueue (EventQueue): Event queue on which the events related to
this request will arrive
Returns:
CorrelationId: The correlation id used to identify the Events
generated as a result of this call
Send the specified ``authorizationRequest`` and update the specified
``identity`` with the results. If the optionally specified
``correlationId`` is supplied, it is used; otherwise create a
:class:`CorrelationId`. The actual :class:`CorrelationId` used is
returned. If the optionally specified ``eventQueue`` is supplied all
:class:`Event`\ s relating to this :class:`Request` will arrive on that
:class:`EventQueue`.
The underlying user information must remain valid until the
Request has completed successfully or failed.
A successful request will generate zero or more
:attr:`~Event.PARTIAL_RESPONSE` :class:`Message`\ s followed by
exactly one :attr:`~Event.RESPONSE` :class:`Message`. Once the final
:attr:`~Event.RESPONSE` :class:`Message` has been received the
specified ``identity`` will have been updated to contain the users
entitlement information and the :class:`CorrelationId` associated with
the request may be re-used. If the request fails at any stage a
:class:`~Event.REQUEST_STATUS` will be generated, the specified
``identity`` will not be modified and the :class:`CorrelationId` may be
re-used.
The ``identity`` supplied must have been returned from this Session's
:meth:`createIdentity()` method and must not be the session identity.
"""
if correlationId is None:
correlationId = CorrelationId()
_ExceptionUtil.raiseOnError(
internals.blpapi_AbstractSession_sendAuthorizationRequest(
self.__handle,
get_handle(request),
get_handle(identity),
get_handle(correlationId),
get_handle(eventQueue),
None # no request label
))
if eventQueue is not None:
eventQueue._registerSession(self)
return correlationId
def cancel(self, correlationId):
"""Cancel ``correlationId`` request.
Args:
correlationId (CorrelationId or [CorrelationId]): Correlation id
associated with the request to cancel
If the specified ``correlationId`` identifies a current
request then cancel that request.
Once this call returns the specified ``correlationId`` will not be seen
in any subsequent :class:`Message` obtained from a
``MessageIterator`` by calling ``next()``.
However, any :class:`Message` currently pointed to by a
``MessageIterator`` when :meth:`cancel()` is called is not
affected even if it has the specified ``correlationId``. Also any
:class:`Message` where a reference has been retained by the application
may still contain the ``correlationId``. For these reasons, although
technically an application is free to re-use ``correlationId`` as soon
as this method returns it is preferable not to aggressively re-use
correlation IDs, particularly with an asynchronous Session.
"""
_ExceptionUtil.raiseOnError(internals.blpapi_AbstractSession_cancel(
self.__handle,
get_handle(correlationId),
1, # number of correlation IDs supplied
None)) # no request label
def generateToken(self, correlationId=None,
eventQueue=None, authId=None, ipAddress=None):
"""Generate a token to be used for authorization.
Args:
correlationId (CorrelationId): Correlation id to be associated with
the request
eventQueue (EventQueue): Event queue on which to receive Events
related to this request
authId (str): The id used for authentication
ipAddress (str): IP of the machine used for authentication
Returns:
CorrelationId: The correlation id used to identify the Events
generated as a result of this call
Raises:
InvalidArgumentException: If the authentication options in
:class:`SessionOptions` or the arguments to the function are
invalid.
The ``authId`` and ``ipAddress`` must be provided together and can only
be provided if the authentication mode is ``MANUAL``.
"""
if correlationId is None:
correlationId = CorrelationId()
if authId is None and ipAddress is None:
_ExceptionUtil.raiseOnError(
internals.blpapi_AbstractSession_generateToken(
self.__handle,
get_handle(correlationId),
get_handle(eventQueue)))
elif authId is not None and ipAddress is not None:
_ExceptionUtil.raiseOnError(
internals.blpapi_AbstractSession_generateManualToken(
self.__handle,
get_handle(correlationId),
authId,
ipAddress,
get_handle(eventQueue)))
else:
raise exception.InvalidArgumentException(
"'authId' and 'ipAddress' must be provided together", 0)
if eventQueue is not None:
eventQueue._registerSession(self)
return correlationId
def getService(self, serviceName):
"""Return a :class:`Service` object representing the service.
Args:
serviceName (str): Name of the service to retrieve
Returns:
Service: Service identified by the service name
Raises:
InvalidStateException: If the service identified by the specified
``serviceName`` is not open already
The ``serviceName`` must contain a fully qualified service name. That
is, it must be of the form ``//<namespace>/<service-name>``.
"""
errorCode, service = internals.blpapi_AbstractSession_getService(
self.__handle,
serviceName)
_ExceptionUtil.raiseOnError(errorCode)
return Service(service, self)
def createIdentity(self):
"""Create an :class:`Identity` which is valid but has not been
authorized.
Returns:
Identity: Identity which is valid but has not been authorized
"""
return Identity(
internals.blpapi_AbstractSession_createIdentity(self.__handle),
self)
def generateAuthorizedIdentity(self,
authOptions,
correlationId=None):
"""Generates an authorized :class:`Identity` with the specified
``authOptions`` and ``correlationId``.
Args:
authOptions (AuthOptions): Used to generate the :class:`Identity`.
correlationId (CorrelationId): Optional. Will identify the messages
associated with the generated :class:`Identity`.
Returns:
CorrelationId: Identifies the aforementioned events and the
generated :class:`Identity`.
If this is an asynchronous session then an :class:`Event` may be
delivered to the registered ``EventHandler`` before
:meth:`generateAuthorizedIdentity()` has returned.
One or more :attr:`Event.AUTHORIZATION_STATUS` events, zero or more
:attr:`Event.TOKEN_STATUS` events, and zero or more
:attr:`Event.SERVICE_STATUS` events are generated.
The behavior is undefined if either ``authOptions`` or
``correlationId`` is ``None``.
"""
if correlationId is None:
correlationId = CorrelationId()
retcode = internals \
.blpapi_AbstractSession_generateAuthorizedIdentityAsync(
self.__handle,
get_handle(authOptions),
get_handle(correlationId))
_ExceptionUtil.raiseOnError(retcode)
return correlationId
def getAuthorizedIdentity(self, correlationId=None):
"""Returns the authorized :class:`Identity` associated with
``correlationId``. If ``correlationId`` is not given, returns the
session identity.
Args:
correlationId (CorrelationId): Optional. Associated with an
:class:`Identity`.
Returns:
Identity: the :class:`Identity` associated with ``correlationId``.
Raises:
NotFoundException: If there is no :class:`Identity` associated
with ``correlationId``, if the associated :class:`Identity` is
not authorized, or if ``correlationId`` is not given and the
session identity is not authorized.
"""
if correlationId is None:
correlationId = CorrelationId()
retcode, identity_handle = internals \
.blpapi_AbstractSession_getAuthorizedIdentity(
self.__handle,
get_handle(correlationId))
_ExceptionUtil.raiseOnError(retcode)
return Identity(identity_handle, self)
# Protect enumeration constant(s) defined in this class and in classes
# derived from this class from changes:
__copyright__ = """
Copyright 2019. Bloomberg Finance L.P.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: The above
copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""