Skip to content
Snippets Groups Projects
Commit 31d06c61 authored by Vincent Texier's avatar Vincent Texier
Browse files

issue #52 add type hinting everywhere in the code (except on local variables)

parent 9abea36f
Branches
Tags
No related merge requests found
......@@ -18,6 +18,7 @@ Features
* Support HTTP, HTTPS and Web Socket transport for BMA API
* Support `Elasticsearch Duniter4j <https://git.duniter.org/clients/java/duniter4j/blob/master/src/site/markdown/ES.md#request-the-es-node>`_ API
* Duniter signing key
* Sign/verify and encrypt/decrypt messages with the Duniter credentials
Requirements
------------
......
......@@ -114,7 +114,7 @@ async def peering(client: Client) -> dict:
return await client.get(MODULE + '/peering', schema=PEERING_SCHEMA)
async def peers(client: Client, leaves: bool = False, leaf: str = ""):
async def peers(client: Client, leaves: bool = False, leaf: str = "") -> dict:
"""
GET peering entries of every node inside the currency network
......
......@@ -16,6 +16,7 @@
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
# vit
import logging
from duniterpy.api.client import Client
logger = logging.getLogger("duniter/node")
......
......@@ -222,7 +222,7 @@ async def process(client: Client, transaction_signed_raw: str) -> ClientResponse
return await client.post(MODULE + '/process', {'transaction': transaction_signed_raw}, rtype=RESPONSE_AIOHTTP)
async def sources(client: Client, pubkey: str):
async def sources(client: Client, pubkey: str) -> dict:
"""
GET transaction sources
......
......@@ -16,6 +16,7 @@
# Caner Candan <caner@candan.fr>, http://caner.candan.fr
# vit
import logging
from duniterpy.api.client import Client
logger = logging.getLogger("duniter/ud")
......@@ -74,4 +75,3 @@ async def history(client: Client, pubkey: str) -> dict:
:rtype: dict
"""
return await client.get(MODULE + '/history/%s' % pubkey, schema=UD_SCHEMA)
......@@ -18,6 +18,8 @@
# vit
import logging
from aiohttp import ClientWebSocketResponse
from duniterpy.api.bma.blockchain import BLOCK_SCHEMA
from duniterpy.api.client import Client
......@@ -53,21 +55,21 @@ WS_PEER_SCHEMA = {
}
def block(client: Client):
def block(client: Client) -> ClientWebSocketResponse:
"""
Connect to block websocket
:param client: Client to connect to the api
:rtype: aiohttp.ClientWebSocketResponse
:rtype: ClientWebSocketResponse
"""
return client.connect_ws(MODULE + '/block')
def peer(client: Client):
def peer(client: Client) -> ClientWebSocketResponse:
"""
Connect to peer websocket
:param client: Client to connect to the api
:rtype: aiohttp.ClientWebSocketResponse
:rtype: ClientWebSocketResponse
"""
return client.connect_ws(MODULE + '/peer')
......@@ -6,8 +6,8 @@ import json
import logging
from typing import Callable, Union, Any, Optional
import aiohttp
import jsonschema
from aiohttp import ClientResponse, ClientWebSocketResponse, ClientSession
import duniterpy.api.endpoint as endpoint
from .errors import DuniterError
......@@ -67,7 +67,7 @@ def parse_error(text: str) -> Any:
return data
async def parse_response(response: aiohttp.ClientResponse, schema: dict) -> Any:
async def parse_response(response: ClientResponse, schema: dict) -> Any:
"""
Validate and parse the BMA answer
......@@ -124,12 +124,12 @@ class API(object):
return url + path
async def requests_get(self, path: str, **kwargs) -> aiohttp.ClientResponse:
async def requests_get(self, path: str, **kwargs) -> ClientResponse:
"""
Requests GET wrapper in order to use API parameters.
:param path: the request path
:rtype: aiohttp.ClientResponse
:rtype: ClientResponse
"""
logging.debug("Request : {0}".format(self.reverse_url(self.connection_handler.http_scheme, path)))
url = self.reverse_url(self.connection_handler.http_scheme, path)
......@@ -145,12 +145,12 @@ class API(object):
return response
async def requests_post(self, path: str, **kwargs) -> aiohttp.ClientResponse:
async def requests_post(self, path: str, **kwargs) -> ClientResponse:
"""
Requests POST wrapper in order to use API parameters.
:param path: the request path
:rtype: aiohttp.ClientResponse
:rtype: ClientResponse
"""
if 'self_' in kwargs:
kwargs['self'] = kwargs.pop('self_')
......@@ -165,7 +165,7 @@ class API(object):
)
return response
def connect_ws(self, path: str) -> aiohttp.ClientWebSocketResponse:
def connect_ws(self, path: str) -> ClientWebSocketResponse:
"""
Connect to a websocket in order to use API parameters
......@@ -175,7 +175,7 @@ class API(object):
and close the ClientWebSocketResponse in it.
:param path: the url path
:rtype: aiohttp.ClientWebSocketResponse
:rtype: ClientWebSocketResponse
"""
url = self.reverse_url(self.connection_handler.ws_scheme, path)
return self.connection_handler.session.ws_connect(url, proxy=self.connection_handler.proxy)
......@@ -186,7 +186,7 @@ class Client:
Main class to create an API client
"""
def __init__(self, _endpoint: Union[str, endpoint.Endpoint], session: aiohttp.ClientSession = None,
def __init__(self, _endpoint: Union[str, endpoint.Endpoint], session: ClientSession = None,
proxy: str = None) -> None:
"""
Init Client instance
......@@ -207,7 +207,7 @@ class Client:
# if no user session...
if session is None:
# open a session
self.session = aiohttp.ClientSession()
self.session = ClientSession()
else:
self.session = session
self.proxy = proxy
......@@ -274,12 +274,12 @@ class Client:
elif rtype == RESPONSE_JSON:
return await response.json()
def connect_ws(self, path: str) -> aiohttp.ClientWebSocketResponse:
def connect_ws(self, path: str) -> ClientWebSocketResponse:
"""
Connect to a websocket in order to use API parameters
:param path: the url path
:rtype: aiohttp.ClientWebSocketResponse
:rtype: ClientWebSocketResponse
"""
client = API(self.endpoint.conn_handler(self.session, self.proxy))
return client.connect_ws(path)
......
import re
from typing import Any, Optional
from typing import Any, Optional, TypeVar, Type, Dict
import aiohttp
from aiohttp import ClientSession
from ..constants import *
from ..documents import MalformedDocumentError
......@@ -11,7 +11,7 @@ class ConnectionHandler:
"""Helper class used by other API classes to ease passing server connection information."""
def __init__(self, http_scheme: str, ws_scheme: str, server: str, port: int, path: str,
session: aiohttp.ClientSession, proxy: Optional[str] = None) -> None:
session: ClientSession, proxy: Optional[str] = None) -> None:
"""
Init instance of connection handler
......@@ -35,15 +35,19 @@ class ConnectionHandler:
return 'connection info: %s:%d' % (self.server, self.port)
# required to type hint cls in classmethod
EndpointType = TypeVar('EndpointType', bound='Endpoint')
class Endpoint:
@classmethod
def from_inline(cls, inline: str):
def from_inline(cls: Type[EndpointType], inline: str) -> EndpointType:
raise NotImplementedError("from_inline(..) is not implemented")
def inline(self) -> str:
raise NotImplementedError("inline() is not implemented")
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
raise NotImplementedError("conn_handler is not implemented")
def __str__(self) -> str:
......@@ -53,6 +57,10 @@ class Endpoint:
raise NotImplementedError("__eq__ is not implemented")
# required to type hint cls in classmethod
UnknownEndpointType = TypeVar('UnknownEndpointType', bound='UnknownEndpoint')
class UnknownEndpoint(Endpoint):
API = None
......@@ -61,7 +69,7 @@ class UnknownEndpoint(Endpoint):
self.properties = properties
@classmethod
def from_inline(cls, inline: str):
def from_inline(cls: Type[UnknownEndpointType], inline: str) -> UnknownEndpointType:
"""
Return UnknownEndpoint instance from endpoint string
......@@ -86,8 +94,8 @@ class UnknownEndpoint(Endpoint):
doc += " {0}".format(p)
return doc
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
return ConnectionHandler("", "", "", 0, "", aiohttp.ClientSession())
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
return ConnectionHandler("", "", "", 0, "", ClientSession())
def __str__(self) -> str:
return "{0} {1}".format(self.api, ' '.join(["{0}".format(p) for p in self.properties]))
......@@ -115,6 +123,9 @@ ERROR_SCHEMA = {
"required": ["ucode", "message"]
}
# required to type hint cls in classmethod
BMAEndpointType = TypeVar('BMAEndpointType', bound='BMAEndpoint')
class BMAEndpoint(Endpoint):
API = "BASIC_MERKLED_API"
......@@ -139,7 +150,7 @@ class BMAEndpoint(Endpoint):
self.port = port
@classmethod
def from_inline(cls, inline: str):
def from_inline(cls: Type[BMAEndpointType], inline: str) -> BMAEndpointType:
"""
Return BMAEndpoint instance from endpoint string
......@@ -167,7 +178,7 @@ class BMAEndpoint(Endpoint):
IPv6=(" {0}".format(self.ipv6) if self.ipv6 else ""),
PORT=(" {0}".format(self.port) if self.port else ""))
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -182,20 +193,24 @@ class BMAEndpoint(Endpoint):
return ConnectionHandler("http", "ws", self.ipv4, self.port, "", session, proxy)
def __str__(self):
def __str__(self) -> str:
return self.inline()
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, BMAEndpoint):
return self.server == other.server and self.ipv4 == other.ipv4 \
and self.ipv6 == other.ipv6 and self.port == other.port
else:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((self.server, self.ipv4, self.ipv6, self.port))
# required to type hint cls in classmethod
SecuredBMAEndpointType = TypeVar('SecuredBMAEndpointType', bound='SecuredBMAEndpoint')
class SecuredBMAEndpoint(BMAEndpoint):
API = "BMAS"
re_inline = re.compile(
......@@ -219,7 +234,7 @@ class SecuredBMAEndpoint(BMAEndpoint):
self.path = path
@classmethod
def from_inline(cls, inline: str):
def from_inline(cls: Type[SecuredBMAEndpointType], inline: str) -> SecuredBMAEndpointType:
"""
Return SecuredBMAEndpoint instance from endpoint string
......@@ -247,7 +262,7 @@ class SecuredBMAEndpoint(BMAEndpoint):
inlined = [str(info) for info in (self.server, self.ipv4, self.ipv6, self.port, self.path) if info]
return SecuredBMAEndpoint.API + " " + " ".join(inlined)
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -263,6 +278,10 @@ class SecuredBMAEndpoint(BMAEndpoint):
return ConnectionHandler("https", "wss", self.ipv4, self.port, self.path, session, proxy)
# required to type hint cls in classmethod
WS2PEndpointType = TypeVar('WS2PEndpointType', bound='WS2PEndpoint')
class WS2PEndpoint(Endpoint):
API = "WS2P"
re_inline = re.compile(
......@@ -273,14 +292,14 @@ class WS2PEndpoint(Endpoint):
ipv6_regex=IPV6_REGEX,
path_regex=PATH_REGEX))
def __init__(self, ws2pid, server, port, path):
def __init__(self, ws2pid: str, server: str, port: int, path: str) -> None:
self.ws2pid = ws2pid
self.server = server
self.port = port
self.path = path
@classmethod
def from_inline(cls, inline):
def from_inline(cls: Type[WS2PEndpointType], inline: str) -> WS2PEndpointType:
m = WS2PEndpoint.re_inline.match(inline)
if m is None:
raise MalformedDocumentError(WS2PEndpoint.API)
......@@ -292,11 +311,11 @@ class WS2PEndpoint(Endpoint):
path = ""
return cls(ws2pid, server, port, path)
def inline(self):
def inline(self) -> str:
inlined = [str(info) for info in (self.ws2pid, self.server, self.port, self.path) if info]
return WS2PEndpoint.API + " " + " ".join(inlined)
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -306,32 +325,36 @@ class WS2PEndpoint(Endpoint):
"""
return ConnectionHandler("https", "wss", self.server, self.port, self.path, session, proxy)
def __str__(self):
def __str__(self) -> str:
return self.inline()
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, WS2PEndpoint):
return self.server == other.server and self.ws2pid == other.ws2pid \
and self.port == other.port and self.path == other.path
else:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((self.ws2pid, self.server, self.port, self.path))
# required to type hint cls in classmethod
ESCoreEndpointType = TypeVar('ESCoreEndpointType', bound='ESCoreEndpoint')
class ESCoreEndpoint(Endpoint):
API = "ES_CORE_API"
re_inline = re.compile(
'^ES_CORE_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
ipv4_regex=IPV4_REGEX))
def __init__(self, server, port):
def __init__(self, server: str, port: int) -> None:
self.server = server
self.port = port
@classmethod
def from_inline(cls, inline):
def from_inline(cls: Type[ESCoreEndpointType], inline: str) -> ESCoreEndpointType:
m = ESCoreEndpoint.re_inline.match(inline)
if m is None:
raise MalformedDocumentError(ESCoreEndpoint.API)
......@@ -339,11 +362,11 @@ class ESCoreEndpoint(Endpoint):
port = int(m.group(2))
return cls(server, port)
def inline(self):
def inline(self) -> str:
inlined = [str(info) for info in (self.server, self.port) if info]
return ESCoreEndpoint.API + " " + " ".join(inlined)
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -353,31 +376,35 @@ class ESCoreEndpoint(Endpoint):
"""
return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
def __str__(self):
def __str__(self) -> str:
return self.inline()
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, ESCoreEndpoint):
return self.server == other.server and self.port == other.port
else:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((self.server, self.port))
# required to type hint cls in classmethod
ESUserEndpointType = TypeVar('ESUserEndpointType', bound='ESUserEndpoint')
class ESUserEndpoint(Endpoint):
API = "ES_USER_API"
re_inline = re.compile(
'^ES_USER_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
ipv4_regex=IPV4_REGEX))
def __init__(self, server, port):
def __init__(self, server: str, port: int) -> None:
self.server = server
self.port = port
@classmethod
def from_inline(cls, inline):
def from_inline(cls: Type[ESUserEndpointType], inline: str) -> ESUserEndpointType:
m = ESUserEndpoint.re_inline.match(inline)
if m is None:
raise MalformedDocumentError(ESUserEndpoint.API)
......@@ -385,11 +412,11 @@ class ESUserEndpoint(Endpoint):
port = int(m.group(2))
return cls(server, port)
def inline(self):
def inline(self) -> str:
inlined = [str(info) for info in (self.server, self.port) if info]
return ESUserEndpoint.API + " " + " ".join(inlined)
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -399,31 +426,35 @@ class ESUserEndpoint(Endpoint):
"""
return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
def __str__(self):
def __str__(self) -> str:
return self.inline()
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, ESUserEndpoint):
return self.server == other.server and self.port == other.port
else:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((self.server, self.port))
# required to type hint cls in classmethod
ESSubscribtionEndpointType = TypeVar('ESSubscribtionEndpointType', bound='ESSubscribtionEndpoint')
class ESSubscribtionEndpoint(Endpoint):
API = "ES_SUBSCRIPTION_API"
re_inline = re.compile(
'^ES_SUBSCRIPTION_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=HOST_REGEX,
ipv4_regex=IPV4_REGEX))
def __init__(self, server, port):
def __init__(self, server: str, port: int) -> None:
self.server = server
self.port = port
@classmethod
def from_inline(cls, inline):
def from_inline(cls: Type[ESSubscribtionEndpointType], inline: str) -> ESSubscribtionEndpointType:
m = ESSubscribtionEndpoint.re_inline.match(inline)
if m is None:
raise MalformedDocumentError(ESSubscribtionEndpoint.API)
......@@ -431,11 +462,11 @@ class ESSubscribtionEndpoint(Endpoint):
port = int(m.group(2))
return cls(server, port)
def inline(self):
def inline(self) -> str:
inlined = [str(info) for info in (self.server, self.port) if info]
return ESSubscribtionEndpoint.API + " " + " ".join(inlined)
def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler:
"""
Return connection handler instance for the endpoint
......@@ -445,16 +476,16 @@ class ESSubscribtionEndpoint(Endpoint):
"""
return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
def __str__(self):
def __str__(self) -> str:
return self.inline()
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, ESSubscribtionEndpoint):
return self.server == other.server and self.port == other.port
else:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((ESSubscribtionEndpoint.API, self.server, self.port))
......@@ -465,10 +496,10 @@ MANAGED_API = {
ESCoreEndpoint.API: ESCoreEndpoint,
ESUserEndpoint.API: ESUserEndpoint,
ESSubscribtionEndpoint.API: ESSubscribtionEndpoint
}
} # type: Dict[str, Any]
def endpoint(value):
def endpoint(value: Any) -> Any:
if issubclass(type(value), Endpoint):
return value
elif isinstance(value, str):
......
......@@ -55,6 +55,7 @@ WS2P_HEADS_SCHEMA = {
}
# fixme: ws2p heads support must be handled by websocket
def heads(client: Client):
"""
GET Certification data over a member
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment