diff --git a/src/linkahead/connection/connection.py b/src/linkahead/connection/connection.py
index 91b4a01da455d0f365e39b0b0f7359e07096e707..140cb8cd46ad0f850fb7543634d0e31d4b7139ce 100644
--- a/src/linkahead/connection/connection.py
+++ b/src/linkahead/connection/connection.py
@@ -23,7 +23,7 @@
 # ** end header
 #
 """Connection to a LinkAhead server."""
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import absolute_import, print_function, unicode_literals, annotations
 
 import logging
 import ssl
@@ -32,7 +32,7 @@ import warnings
 from builtins import str  # pylint: disable=redefined-builtin
 from errno import EPIPE as BrokenPipe
 from socket import error as SocketError
-from urllib.parse import quote, urlparse
+from urllib.parse import ParseResult, quote, urlparse
 from warnings import warn
 
 from requests import Session as HTTPSession
@@ -58,16 +58,24 @@ from .encode import MultipartYielder, ReadableMultiparts
 from .interface import CaosDBHTTPResponse, CaosDBServerConnection
 from .utils import make_uri_path, parse_url, urlencode
 
+from typing import TYPE_CHECKING
+if TYPE_CHECKING and sys.version_info > (3, 7):
+    from typing import Optional, List, Any, Iterator, Dict, Union
+    from requests.models import Response
+    from ssl import _SSLMethod
+    from .authentication.interface import AbstractAuthenticator
+
+
 _LOGGER = logging.getLogger(__name__)
 
 
 class _WrappedHTTPResponse(CaosDBHTTPResponse):
 
-    def __init__(self, response):
-        self.response = response
-        self._generator = None
-        self._buffer = b''
-        self._stream_consumed = False
+    def __init__(self, response: Response):
+        self.response: Response = response
+        self._generator: Optional[Iterator[Any]] = None
+        self._buffer: Optional[bytes] = b''
+        self._stream_consumed: bool = False
 
     @property
     def reason(self):
@@ -77,7 +85,7 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse):
     def status(self):
         return self.response.status_code
 
-    def read(self, size=None):
+    def read(self, size: Optional[int] = None):
         if self._stream_consumed is True:
             raise RuntimeError("Stream is consumed")
 
@@ -91,6 +99,10 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse):
             self._stream_consumed = True
             return self.response.content
 
+        if size is None or size == 0:
+            raise RuntimeError(
+                "size parameter should not be None if the stream is not consumed yet")
+
         if len(self._buffer) >= size:
             # still enough bytes in the buffer
             result = chunk[:size]
@@ -117,7 +129,7 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse):
             self._buffer = None
             return result
 
-    def getheader(self, name, default=None):
+    def getheader(self, name: str, default=None):
         return self.response.headers[name] if name in self.response.headers else default
 
     def getheaders(self):
@@ -130,7 +142,7 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse):
 class _SSLAdapter(HTTPAdapter):
     """Transport adapter that allows us to use different SSL versions."""
 
-    def __init__(self, ssl_version):
+    def __init__(self, ssl_version: _SSLMethod):
         self.ssl_version = ssl_version
         super().__init__()
 
@@ -156,7 +168,11 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
         self._session = None
         self._timeout = None
 
-    def request(self, method, path, headers=None, body=None):
+    def request(self,
+                method: str, path: str,
+                headers: Optional[Dict[str, str]] = None,
+                body: Union[str, bytes, None] = None,
+                **kwargs):
         """request.
 
         Send a HTTP request to the server.
@@ -169,7 +185,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
             An URI path segment (without the 'scheme://host:port/' parts),
             including query and frament segments.
         headers : dict of str -> str, optional
-            HTTP request headers. (Defautl: None)
+            HTTP request headers. (Default: None)
         body : str or bytes or readable, optional
             The body of the HTTP request. Bytes should be a utf-8 encoded
             string.
@@ -232,14 +248,15 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
                 "No connection url specified. Please "
                 "do so via linkahead.configure_connection(...) or in a config "
                 "file.")
-        if (not config["url"].lower().startswith("https://") and not config["url"].lower().startswith("http://")):
+        url_string: str = config["url"]
+        if (not url_string.lower().startswith("https://") and not url_string.lower().startswith("http://")):
             raise LinkAheadConnectionError("The connection url is expected "
                                            "to be a http or https url and "
                                            "must include the url scheme "
                                            "(i.e. start with https:// or "
                                            "http://).")
 
-        url = urlparse(config["url"])
+        url: ParseResult = urlparse(url=url_string)
         path = url.path.strip("/")
         if len(path) > 0:
             path = path + "/"
@@ -271,7 +288,7 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
         if "timeout" in config:
             self._timeout = config["timeout"]
 
-    def _setup_ssl(self, config):
+    def _setup_ssl(self, config: Dict[str, Any]):
         if "ssl_version" in config and config["cacert"] is not None:
             ssl_version = getattr(ssl, config["ssl_version"])
         else:
@@ -325,7 +342,7 @@ _DEFAULT_CONF = {
 }
 
 
-def _get_authenticator(**config):
+def _get_authenticator(**config) -> AbstractAuthenticator:
     """_get_authenticator.
 
     Import and configure the password_method.
@@ -337,7 +354,7 @@ def _get_authenticator(**config):
         Currently, there are four valid values for this parameter: 'plain',
         'pass', 'keyring' and 'auth_token'.
     **config :
-        Any other keyword arguments are passed the configre method of the
+        Any other keyword arguments are passed the configure method of the
         password_method.
 
     Returns
@@ -534,8 +551,8 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
     __instance = None
 
     def __init__(self):
-        self._delegate_connection = None
-        self._authenticator = None
+        self._delegate_connection: Optional[CaosDBServerConnection] = None
+        self._authenticator: Optional[AbstractAuthenticator] = None
         self.is_configured = False
 
     @classmethod
@@ -553,7 +570,8 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
                 "Missing CaosDBServerConnection implementation. You did not "
                 "specify an `implementation` for the connection.")
         try:
-            self._delegate_connection = config["implementation"]()
+            self._delegate_connection: CaosDBServerConnection = config["implementation"](
+            )
 
             if not isinstance(self._delegate_connection,
                               CaosDBServerConnection):
@@ -579,7 +597,10 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
 
         return self
 
-    def retrieve(self, entity_uri_segments=None, query_dict=None, **kwargs):
+    def retrieve(self,
+                 entity_uri_segments: Optional[List[str]] = None,
+                 query_dict: Optional[Dict[str, Optional[str]]] = None,
+                 **kwargs):
         path = make_uri_path(entity_uri_segments, query_dict)
 
         http_response = self._http_request(method="GET", path=path, **kwargs)
@@ -641,7 +662,7 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
 
         return http_response
 
-    def download_file(self, path):
+    def download_file(self, path: str):
         """This function downloads a file via HTTP from the LinkAhead file
         system."""
         try:
@@ -681,7 +702,11 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
                                                 **kwargs)
             raise
 
-    def _retry_http_request(self, method, path, headers, body, **kwargs):
+    def _retry_http_request(self,
+                            method: str,
+                            path: str,
+                            headers: Optional[Dict["str", Any]],
+                            body: Union[str, bytes], **kwargs) -> CaosDBHTTPResponse:
 
         if hasattr(body, "encode"):
             # python3
@@ -689,8 +714,18 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
 
         if headers is None:
             headers = {}
+
+        if self._authenticator is None:
+            raise ValueError(
+                "No authenticator set. Please call configure_connection() first.")
+
         self._authenticator.on_request(method=method, path=path,
                                        headers=headers)
+
+        if self._delegate_connection is None:
+            raise ValueError(
+                "No connection set. Please call configure_connection() first.")
+
         _LOGGER.debug("request: %s %s %s", method, path, str(headers))
         http_response = self._delegate_connection.request(
             method=method,
@@ -704,10 +739,16 @@ class _Connection(object):  # pylint: disable=useless-object-inheritance
 
         return http_response
 
-    def get_username(self):
+    def get_username(self) -> str:
         """
         Return the username of the current connection.
 
         Shortcut for: get_connection()._authenticator._credentials_provider.username
         """
+        if self._authenticator is None:
+            raise ValueError(
+                "No authenticator set. Please call configure_connection() first.")
+        if self._authenticator._credentials_provider is None:
+            raise ValueError(
+                "No credentials provider set. Please call configure_connection() first.")
         return self._authenticator._credentials_provider.username
diff --git a/src/linkahead/connection/encode.py b/src/linkahead/connection/encode.py
index 6b328285e97e4dce2483ddd955134ee64cd3ce84..68de1be316bf448b893d93e50c8262f26fff0c12 100644
--- a/src/linkahead/connection/encode.py
+++ b/src/linkahead/connection/encode.py
@@ -48,6 +48,7 @@ as multipart/form-data suitable for a HTTP POST or PUT request.
 
 multipart/form-data is the standard way to upload files over HTTP
 """
+from __future__ import annotations
 
 __all__ = [
     'gen_boundary', 'encode_and_quote', 'MultipartParam', 'encode_string',
@@ -61,6 +62,10 @@ import re
 import os
 import mimetypes
 from email.header import Header
+from typing import TYPE_CHECKING
+import sys
+if TYPE_CHECKING and sys.version_info > (3, 7):
+    from typing import Optional
 
 
 def gen_boundary():
@@ -68,7 +73,7 @@ def gen_boundary():
     return uuid.uuid4().hex
 
 
-def encode_and_quote(data):
+def encode_and_quote(data: Optional[str]) -> Optional[str]:
     """If ``data`` is unicode, return urllib.quote_plus(data.encode("utf-8"))
     otherwise return urllib.quote_plus(data)"""
     if data is None:
@@ -111,7 +116,7 @@ class MultipartParam(object):
     """
 
     def __init__(self,
-                 name,
+                 name: str,
                  value=None,
                  filename=None,
                  filetype=None,
diff --git a/src/linkahead/connection/interface.py b/src/linkahead/connection/interface.py
index d63dbeb8cc4cd59e056823440948aa54906dd47c..32e5c8fd340edf14d5653790d3d50cb031b3a56a 100644
--- a/src/linkahead/connection/interface.py
+++ b/src/linkahead/connection/interface.py
@@ -22,11 +22,14 @@
 # ** end header
 #
 """This module defines the CaosDBServerConnection interface."""
-from abc import ABCMeta, abstractmethod, abstractproperty
+from __future__ import annotations
+from abc import ABCMeta, abstractmethod, ABC
 from warnings import warn
 
-# meta class compatible with Python 2 *and* 3:
-ABC = ABCMeta('ABC', (object, ), {'__slots__': ()})
+
+from typing import TYPE_CHECKING
+if TYPE_CHECKING:
+    from typing import Optional, Dict, Union
 
 
 class CaosDBHTTPResponse(ABC):
@@ -34,14 +37,14 @@ class CaosDBHTTPResponse(ABC):
     LinkAheadServer."""
 
     @abstractmethod
-    def read(self, size=-1):
+    def read(self, size: Optional[int] = -1):
         """Read up to *size* bytes from the response body.
 
         If size is unspecified or -1, all bytes until EOF are returned.
         """
 
     @abstractmethod
-    def getheader(self, name, default=None):
+    def getheader(self, name: str, default=None):
         """Return the value of the header *name* or the value of *default* if
         there is no such header.
 
@@ -50,12 +53,13 @@ class CaosDBHTTPResponse(ABC):
         are returned likewise.
         """
 
-    @abstractproperty
-    def status(self):
+    @property
+    @abstractmethod
+    def status(self) -> int:
         """Status code of the response."""
 
     @abstractmethod
-    def getheaders(self):
+    def getheaders(self) -> Dict[str, str]:
         """Return all headers."""
 
     def __enter__(self):
@@ -78,7 +82,12 @@ class CaosDBServerConnection(ABC):
     LinkAhead server."""
 
     @abstractmethod
-    def request(self, method, path, headers=None, body=None, **kwargs):
+    def request(self,
+                method: str,
+                path: str,
+                headers: Optional[Dict[str, str]] = None,
+                body: Union[str, bytes, None] = None,
+                **kwargs) -> CaosDBHTTPResponse:
         """Abstract method. Implement this method for HTTP requests to the
         LinkAhead server.
 
diff --git a/src/linkahead/connection/utils.py b/src/linkahead/connection/utils.py
index 90ec6b5ba6789747f5d4452a1260306b716b1f7e..bfb27a7278b30e20068cc9823aa49385aef08ec5 100644
--- a/src/linkahead/connection/utils.py
+++ b/src/linkahead/connection/utils.py
@@ -22,14 +22,19 @@
 # ** end header
 #
 """Utility functions for the connection module."""
-from __future__ import unicode_literals, print_function
+from __future__ import unicode_literals, print_function, annotations
 from builtins import str as unicode
 from urllib.parse import (urlencode as _urlencode, quote as _quote,
                           urlparse, urlunparse, unquote as _unquote)
 import re
 
+from typing import TYPE_CHECKING
+import sys
+if TYPE_CHECKING and sys.version_info > (3, 7):
+    from typing import Optional, Dict, List
 
-def urlencode(query):
+
+def urlencode(query: Dict[str, Optional[str]]) -> str:
     """Convert a dict of into a url-encoded (unicode) string.
 
     This is basically a python2/python3 compatibility wrapper for the respective
@@ -79,7 +84,8 @@ modules when they are called with only the query parameter.
         }))
 
 
-def make_uri_path(segments=None, query=None):
+def make_uri_path(segments: Optional[List[str]] = None,
+                  query: Optional[Dict[str, Optional[str]]] = None) -> str:
     """Url-encode all segments, concat them with slashes and append the query.
 
     Examples
@@ -105,7 +111,10 @@ def make_uri_path(segments=None, query=None):
     """
     path_no_query = ("/".join([quote(segment) for segment in segments])
                      if segments else "")
-    return str(path_no_query if query is None else "?".join([
+    if query is None:
+        return str(path_no_query)
+
+    return str("?".join([
         path_no_query, "&".join([
             quote(key) + "=" +
             (quote(query[key]) if query[key] is not None else "")
@@ -114,13 +123,13 @@ def make_uri_path(segments=None, query=None):
     ]))
 
 
-def quote(string):
+def quote(string: str) -> str:
     enc = string.encode('utf-8')
     return _quote(enc).replace('/', '%2F')
 
 
-def parse_url(url):
-    fullurl = urlparse(url)
+def parse_url(url: str):
+    fullurl = urlparse(url=url)
     # make sure the path ends with a slash
     if not fullurl.path.endswith("/"):
         parse_result = list(fullurl)
@@ -132,7 +141,7 @@ def parse_url(url):
 _PATTERN = re.compile(r"^SessionToken=([^;]*);.*$")
 
 
-def unquote(string):
+def unquote(string) -> str:
     """unquote.
 
     Decode an urlencoded string into a plain text string.
@@ -144,7 +153,7 @@ def unquote(string):
     return bts
 
 
-def parse_auth_token(cookie):
+def parse_auth_token(cookie: Optional[str]) -> Optional[str]:
     """parse_auth_token.
 
     Parse an auth token from a cookie.
@@ -165,7 +174,7 @@ def parse_auth_token(cookie):
     return auth_token
 
 
-def auth_token_to_cookie(auth_token):
+def auth_token_to_cookie(auth_token: str) -> str:
     """auth_token_to_cookie.
 
     Urlencode an auth token string and format it as a cookie.