From 9a46039f6c7fd59beab553f32d6827e35ced56e6 Mon Sep 17 00:00:00 2001
From: Vincent Texier <vit@free.fr>
Date: Thu, 19 Jul 2018 19:25:38 +0200
Subject: [PATCH] issue #52 use mypy to check static typing

Update README with make check command
---
 .gitignore                |  2 +-
 Makefile                  |  5 ++++-
 README.rst                | 14 +++++++++----
 duniterpy/api/client.py   | 18 ++++++++---------
 duniterpy/api/endpoint.py | 41 ++++++++++++++++++++-------------------
 requirements_dev.txt      |  2 +-
 6 files changed, 46 insertions(+), 36 deletions(-)

diff --git a/.gitignore b/.gitignore
index 07e928e3..22e92cd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,4 +38,4 @@ nosetests.xml
 .python-version
 docs/_build
 docs/duniterpy.*
-
+.mypy_cache
diff --git a/Makefile b/Makefile
index e14bc3e2..d615fe9c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: docs tests
+.PHONY: docs tests check
 
 # generate documentation
 docs:
@@ -8,3 +8,6 @@ docs:
 tests:
 	python -m unittest
 
+# check static typing
+check:
+	mypy duniterpy --ignore-missing-imports
diff --git a/README.rst b/README.rst
index 44bf9cb0..72eaf65c 100644
--- a/README.rst
+++ b/README.rst
@@ -44,15 +44,21 @@ Development
     pip install -r requirements.txt
 
 * Add PYTHONPATH env var to your shell containing the path to this repository
-* Run unit tests with::
-
-    make tests
-
 * Take a look at examples folder
 * Run examples from parent folder::
 
     python examples/request_data.py
 
+* Before submit a merge requests, please check the static typing and tests.
+
+* Check static typing with `mypy <http://mypy-lang.org/>`_::
+
+    make check
+
+* Run unit tests with::
+
+    make tests
+
 Documentation
 -------------
 
diff --git a/duniterpy/api/client.py b/duniterpy/api/client.py
index f1b53e85..ff796ab2 100644
--- a/duniterpy/api/client.py
+++ b/duniterpy/api/client.py
@@ -4,7 +4,7 @@
 # vit
 import json
 import logging
-from typing import Callable, Union
+from typing import Callable, Union, Any
 
 import aiohttp
 import jsonschema
@@ -34,7 +34,7 @@ ERROR_SCHEMA = {
 }
 
 
-def parse_text(text: str, schema: dict) -> any:
+def parse_text(text: str, schema: dict) -> Any:
     """
     Validate and parse the BMA answer from websocket
 
@@ -51,7 +51,7 @@ def parse_text(text: str, schema: dict) -> any:
     return data
 
 
-def parse_error(text: str) -> any:
+def parse_error(text: str) -> Any:
     """
     Validate and parse the BMA answer from websocket
 
@@ -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: aiohttp.ClientResponse, schema: dict) -> Any:
     """
     Validate and parse the BMA answer
 
@@ -91,7 +91,7 @@ class API(object):
     """
     schema = {}
 
-    def __init__(self, connection_handler: endpoint.ConnectionHandler, module: str):
+    def __init__(self, connection_handler: endpoint.ConnectionHandler, module: str) -> None:
         """
         Asks a module in order to create the url used then by derivated classes.
 
@@ -189,7 +189,7 @@ class Client:
     """
 
     def __init__(self, _endpoint: Union[str, endpoint.Endpoint], session: aiohttp.ClientSession = None,
-                 proxy: str = None):
+                 proxy: str = None) -> None:
         """
         Init Client instance
 
@@ -214,7 +214,7 @@ class Client:
             self.session = session
         self.proxy = proxy
 
-    async def get(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> any:
+    async def get(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> Any:
         """
         GET request on self.endpoint + url_path
 
@@ -245,7 +245,7 @@ class Client:
         elif rtype == RESPONSE_JSON:
             return await response.json()
 
-    async def post(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> any:
+    async def post(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> Any:
         """
         POST request on self.endpoint + url_path
 
@@ -294,7 +294,7 @@ class Client:
         """
         await self.session.close()
 
-    def __call__(self, _function: Callable, *args: any, **kwargs: any) -> any:
+    def __call__(self, _function: Callable, *args: Any, **kwargs: Any) -> Any:
         """
         Call the _function given with the args given
         So we can call many packages wrapping the REST API
diff --git a/duniterpy/api/endpoint.py b/duniterpy/api/endpoint.py
index 0d1acd8b..42e96f5d 100644
--- a/duniterpy/api/endpoint.py
+++ b/duniterpy/api/endpoint.py
@@ -1,4 +1,5 @@
 import re
+from typing import Any, Optional
 
 import aiohttp
 
@@ -9,8 +10,8 @@ from ..documents import MalformedDocumentError
 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 = "", proxy: str = None,
-                 session: aiohttp.ClientSession = None):
+    def __init__(self, http_scheme: str, ws_scheme: str, server: str, port: int, path: str,
+                 session: aiohttp.ClientSession, proxy: Optional[str] = None) -> None:
         """
         Init instance of connection handler
 
@@ -18,9 +19,9 @@ class ConnectionHandler:
         :param ws_scheme: Web socket scheme
         :param server: Server IP or domain name
         :param port: Port number
-        :param port: Url path (optional, default="")
+        :param port: Url path
+        :param session: Session AIOHTTP
         :param proxy: Proxy (optional, default=None)
-        :param session: Session AIOHTTP (optional, default=None)
         """
         self.http_scheme = http_scheme
         self.ws_scheme = ws_scheme
@@ -48,14 +49,14 @@ class Endpoint:
     def __str__(self) -> str:
         raise NotImplementedError("__str__ is not implemented")
 
-    def __eq__(self, other: any) -> bool:
+    def __eq__(self, other: Any) -> bool:
         raise NotImplementedError("__eq__ is not implemented")
 
 
 class UnknownEndpoint(Endpoint):
     API = None
 
-    def __init__(self, api: str, properties: list):
+    def __init__(self, api: str, properties: list) -> None:
         self.api = api
         self.properties = properties
 
@@ -86,12 +87,12 @@ class UnknownEndpoint(Endpoint):
         return doc
 
     def conn_handler(self, session: aiohttp.ClientSession, proxy: str = None) -> ConnectionHandler:
-        return ConnectionHandler("", "", "", 0, "")
+        return ConnectionHandler("", "", "", 0, "", aiohttp.ClientSession())
 
     def __str__(self) -> str:
         return "{0} {1}".format(self.api, ' '.join(["{0}".format(p) for p in self.properties]))
 
-    def __eq__(self, other: any) -> bool:
+    def __eq__(self, other: Any) -> bool:
         if isinstance(other, UnknownEndpoint):
             return self.api == other.api and self.properties == other.properties
         else:
@@ -123,7 +124,7 @@ class BMAEndpoint(Endpoint):
             ipv4_regex=IPV4_REGEX,
             ipv6_regex=IPV6_REGEX))
 
-    def __init__(self, server: str, ipv4: str, ipv6: str, port: int):
+    def __init__(self, server: str, ipv4: str, ipv6: str, port: int) -> None:
         """
         Init BMAEndpoint instance
 
@@ -175,11 +176,11 @@ class BMAEndpoint(Endpoint):
         :return:
         """
         if self.server:
-            return ConnectionHandler("http", "ws", self.server, self.port, "", proxy, session)
+            return ConnectionHandler("http", "ws", self.server, self.port, "", session, proxy)
         elif self.ipv6:
-            return ConnectionHandler("http", "ws", "[{0}]".format(self.ipv6), self.port, "", proxy, session)
+            return ConnectionHandler("http", "ws", "[{0}]".format(self.ipv6), self.port, "", session, proxy)
 
-        return ConnectionHandler("http", "ws", self.ipv4, self.port, "", proxy, session)
+        return ConnectionHandler("http", "ws", self.ipv4, self.port, "", session, proxy)
 
     def __str__(self):
         return self.inline()
@@ -204,7 +205,7 @@ class SecuredBMAEndpoint(BMAEndpoint):
             ipv6_regex=IPV6_REGEX,
             path_regex=PATH_REGEX))
 
-    def __init__(self, server: str, ipv4: str, ipv6: str, port: int, path: str):
+    def __init__(self, server: str, ipv4: str, ipv6: str, port: int, path: str) -> None:
         """
         Init SecuredBMAEndpoint instance
 
@@ -255,11 +256,11 @@ class SecuredBMAEndpoint(BMAEndpoint):
         :return:
         """
         if self.server:
-            return ConnectionHandler("https", "wss", self.server, self.port, self.path, proxy, session)
+            return ConnectionHandler("https", "wss", self.server, self.port, self.path, session, proxy)
         elif self.ipv6:
-            return ConnectionHandler("https", "wss", "[{0}]".format(self.ipv6), self.port, self.path, proxy, session)
+            return ConnectionHandler("https", "wss", "[{0}]".format(self.ipv6), self.port, self.path, session, proxy)
 
-        return ConnectionHandler("https", "wss", self.ipv4, self.port, self.path, proxy, session)
+        return ConnectionHandler("https", "wss", self.ipv4, self.port, self.path, session, proxy)
 
 
 class WS2PEndpoint(Endpoint):
@@ -303,7 +304,7 @@ class WS2PEndpoint(Endpoint):
         :param str proxy: Proxy url
         :rtype: ConnectionHandler
         """
-        return ConnectionHandler("https", "wss", self.server, self.port, self.path, proxy, session)
+        return ConnectionHandler("https", "wss", self.server, self.port, self.path, session, proxy)
 
     def __str__(self):
         return self.inline()
@@ -350,7 +351,7 @@ class ESCoreEndpoint(Endpoint):
         :param str proxy: Proxy url
         :rtype: ConnectionHandler
         """
-        return ConnectionHandler("https", "wss", self.server, self.port, "", proxy, session)
+        return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
 
     def __str__(self):
         return self.inline()
@@ -396,7 +397,7 @@ class ESUserEndpoint(Endpoint):
         :param str proxy: Proxy url
         :rtype: ConnectionHandler
         """
-        return ConnectionHandler("https", "wss", self.server, self.port, "", proxy, session)
+        return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
 
     def __str__(self):
         return self.inline()
@@ -442,7 +443,7 @@ class ESSubscribtionEndpoint(Endpoint):
         :param str proxy: Proxy url
         :rtype: ConnectionHandler
         """
-        return ConnectionHandler("https", "wss", self.server, self.port, "", proxy, session)
+        return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy)
 
     def __str__(self):
         return self.inline()
diff --git a/requirements_dev.txt b/requirements_dev.txt
index fe4bbdea..ff19235a 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -1,2 +1,2 @@
 sphinx
-
+mypy
-- 
GitLab