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 / abstractsession.py
Size: Mime:
# 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.
"""