Skip to content

Commit

Permalink
Merge branch 'feature/type-hints' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
robertmrk committed Jan 4, 2019
2 parents 7393095 + 62c72af commit 3846359
Show file tree
Hide file tree
Showing 17 changed files with 452 additions and 301 deletions.
25 changes: 25 additions & 0 deletions aiocometd/_typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Type definitions"""
from typing import List, Union, Callable, Awaitable, Any, Dict
import ssl as ssl_module

import aiohttp

from .constants import ConnectionType


#: Coroutine function
CoroFunction = Callable[..., Awaitable[Any]]
#: JSON object value
JsonObject = Dict[str, Any]
#: JSON serializer function
JsonDumper = Callable[[JsonObject], str]
#: JSON deserializer function
JsonLoader = Callable[[str], JsonObject]
#: Message payload (list of messages)
Payload = List[JsonObject]
#: Header values
Headers = Dict[str, str]
#: Connection type specification
ConnectionTypeSpec = Union[ConnectionType, List[ConnectionType]]
#: SSL validation mode
SSLValidationMode = Union[ssl_module.SSLContext, aiohttp.Fingerprint, bool]
118 changes: 67 additions & 51 deletions aiocometd/client.py

Large diffs are not rendered by default.

42 changes: 27 additions & 15 deletions aiocometd/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
TransportConnectionClosed
ServerError
"""
from typing import Optional, List, cast

from . import utils


Expand Down Expand Up @@ -39,47 +41,57 @@ class TransportConnectionClosed(TransportError):


class ServerError(AiocometdException):
"""CometD server side error
"""CometD server side error"""
# pylint: disable=useless-super-delegation
def __init__(self, message: str, response: Optional[utils.JsonObject]) \
-> None:
"""If the *response* contains an error field it gets parsed
according to the \
`specs <https://docs.cometd.org/current/reference/#_code_error_code>`_
If the *response* contains an error field it gets parsed
according to the \
`specs <https://docs.cometd.org/current/reference/#_code_error_code>`_
:param message: Error description
:param response: Server response message
"""
super().__init__(message, response)

:param str message: Error description
:param dict response: Server response message
"""
# pylint: enable=useless-super-delegation

@property
def message(self):
def message(self) -> str:
"""Error description"""
return self.args[0] # pylint: disable=unsubscriptable-object
# pylint: disable=unsubscriptable-object
return cast(str, self.args[0])
# pylint: enable=unsubscriptable-object

@property
def response(self):
def response(self) -> Optional[utils.JsonObject]:
"""Server response message"""
return self.args[1] # pylint: disable=unsubscriptable-object
return cast(Optional[utils.JsonObject],
self.args[1]) # pylint: disable=unsubscriptable-object

@property
def error(self):
def error(self) -> Optional[str]:
"""Error field in the :obj:`response`"""
if self.response is None:
return None
return self.response.get("error")

@property
def error_code(self):
def error_code(self) -> Optional[int]:
"""Error code part of the error code part of the `error\
<https://docs.cometd.org/current/reference/#_code_error_code>`_, \
message field"""
return utils.get_error_code(self.error)

@property
def error_message(self):
def error_message(self) -> Optional[str]:
"""Description part of the `error\
<https://docs.cometd.org/current/reference/#_code_error_code>`_, \
message field"""
return utils.get_error_message(self.error)

@property
def error_args(self):
def error_args(self) -> Optional[List[str]]:
"""Arguments part of the `error\
<https://docs.cometd.org/current/reference/#_code_error_code>`_, \
message field"""
Expand Down
17 changes: 10 additions & 7 deletions aiocometd/extensions.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
"""Extension classes"""
from abc import ABC, abstractmethod
from typing import Optional

from ._typing import Payload, Headers


class Extension(ABC):
"""Defines operations supported by extensions"""
@abstractmethod
async def outgoing(self, payload, headers):
async def outgoing(self, payload: Payload, headers: Headers) -> None:
"""Process outgoing *payload* and *headers*
Called just before a payload is sent
:param list[dict] payload: List of outgoing messages
:param dict headers: Headers to send
:param payload: List of outgoing messages
:param headers: Headers to send
"""

@abstractmethod
async def incoming(self, payload, headers=None):
async def incoming(self, payload: Payload,
headers: Optional[Headers] = None) -> None:
"""Process incoming *payload* and *headers*
Called just after a payload is received
:param list[dict] payload: List of incoming messages
:param payload: List of incoming messages
:param headers: Headers to send
:type headers: dict or None
"""


class AuthExtension(Extension): # pylint: disable=abstract-method
"""Extension with support for authentication"""
async def authenticate(self):
async def authenticate(self) -> None:
"""Called after a failed authentication attempt
For authentication schemes where the credentials are static it doesn't
Expand Down
56 changes: 31 additions & 25 deletions aiocometd/transports/abc.py
Original file line number Diff line number Diff line change
@@ -1,119 +1,125 @@
"""Transport abstract base class definition"""
from abc import ABC, abstractmethod
from typing import Set, Optional, List

from ..constants import ConnectionType, TransportState
from .._typing import JsonObject


class Transport(ABC):
"""Defines the operations that all transport classes should support"""
@property
@abstractmethod
def connection_type(self):
def connection_type(self) -> ConnectionType:
"""The transport's connection type"""

@property
@abstractmethod
def endpoint(self):
def endpoint(self) -> str:
"""CometD service url"""

@property
@abstractmethod
def client_id(self):
def client_id(self) -> Optional[str]:
"""Clinet id value assigned by the server"""

@property
@abstractmethod
def state(self):
def state(self) -> TransportState:
"""Current state of the transport"""

@property
@abstractmethod
def subscriptions(self):
def subscriptions(self) -> Set[str]:
"""Set of subscribed channels"""

@property
@abstractmethod
def last_connect_result(self):
def last_connect_result(self) -> Optional[JsonObject]:
"""Result of the last connect request"""

@property
@abstractmethod
def reconnect_advice(self) -> JsonObject:
"""Reconnection advice parameters returned by the server"""

@abstractmethod
async def handshake(self, connection_types):
async def handshake(self, connection_types: List[ConnectionType]) \
-> JsonObject:
"""Executes the handshake operation
:param list[ConnectionType] connection_types: list of connection types
:param connection_types: list of connection types
:return: Handshake response
:rtype: dict
:raises TransportError: When the network request fails.
"""

@abstractmethod
async def connect(self):
async def connect(self) -> JsonObject:
"""Connect to the server
The transport will try to start and maintain a continuous connection
with the server, but it'll return with the response of the first
successful connection as soon as possible.
:return dict: The response of the first successful connection.
:return: The response of the first successful connection.
:raise TransportInvalidOperation: If the transport doesn't has a \
client id yet, or if it's not in a :obj:`~TransportState.DISCONNECTED`\
:obj:`state`.
:raises TransportError: When the network request fails.
"""

@abstractmethod
async def disconnect(self):
async def disconnect(self) -> None:
"""Disconnect from server
The disconnect message is only sent to the server if the transport is
actually connected.
"""

@abstractmethod
async def close(self):
async def close(self) -> None:
"""Close transport and release resources"""

@abstractmethod
async def subscribe(self, channel):
async def subscribe(self, channel: str) -> JsonObject:
"""Subscribe to *channel*
:param str channel: Name of the channel
:param channel: Name of the channel
:return: Subscribe response
:rtype: dict
:raise TransportInvalidOperation: If the transport is not in the \
:obj:`~TransportState.CONNECTED` or :obj:`~TransportState.CONNECTING` \
:obj:`state`
:raises TransportError: When the network request fails.
"""

@abstractmethod
async def unsubscribe(self, channel):
async def unsubscribe(self, channel: str) -> JsonObject:
"""Unsubscribe from *channel*
:param str channel: Name of the channel
:param channel: Name of the channel
:return: Unsubscribe response
:rtype: dict
:raise TransportInvalidOperation: If the transport is not in the \
:obj:`~TransportState.CONNECTED` or :obj:`~TransportState.CONNECTING` \
:obj:`state`
:raises TransportError: When the network request fails.
"""

@abstractmethod
async def publish(self, channel, data):
async def publish(self, channel: str, data: JsonObject) -> JsonObject:
"""Publish *data* to the given *channel*
:param str channel: Name of the channel
:param dict data: Data to send to the server
:param channel: Name of the channel
:param data: Data to send to the server
:return: Publish response
:rtype: dict
:raise TransportInvalidOperation: If the transport is not in the \
:obj:`~TransportState.CONNECTED` or :obj:`~TransportState.CONNECTING` \
:obj:`state`
:raises TransportError: When the network request fails.
"""

@abstractmethod
async def wait_for_state(self, state):
async def wait_for_state(self, state: TransportState) -> None:
"""Waits for and returns when the transport enters the given *state*
:param TransportState state: A state value
:param state: A state value
"""
Loading

0 comments on commit 3846359

Please sign in to comment.