diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ dist version.txt .tox +.mypy_cache/ diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ include requirements.txt include requirements-swh.txt include version.txt +recursive-include swh py.typed diff --git a/mypy.ini b/mypy.ini new file mode 100644 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,21 @@ +[mypy] +namespace_packages = True +warn_unused_ignores = True + + +# 3rd party libraries without stubs (yet) + +[mypy-azure.*] +ignore_missing_imports = True + +[mypy-libcloud.*] +ignore_missing_imports = True + +[mypy-pkg_resources.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True + +[mypy-rados.*] +ignore_missing_imports = True diff --git a/swh/__init__.py b/swh/__init__.py --- a/swh/__init__.py +++ b/swh/__init__.py @@ -1 +1,4 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) +from typing import Iterable + +__path__ = __import__('pkgutil').extend_path(__path__, + __name__) # type: Iterable[str] diff --git a/swh/objstorage/__init__.py b/swh/objstorage/__init__.py --- a/swh/objstorage/__init__.py +++ b/swh/objstorage/__init__.py @@ -13,6 +13,7 @@ from swh.objstorage.backends.seaweed import WeedObjStorage from swh.objstorage.backends.generator import RandomGeneratorObjStorage +from typing import Callable, Dict, Union __all__ = ['get_objstorage', 'ObjStorage'] @@ -23,7 +24,7 @@ 'memory': InMemoryObjStorage, 'weed': WeedObjStorage, 'random': RandomGeneratorObjStorage, -} +} # type: Dict[str, Union[type, Callable[..., type]]] _STORAGE_CLASSES_MISSING = { } diff --git a/swh/objstorage/api/server.py b/swh/objstorage/api/server.py --- a/swh/objstorage/api/server.py +++ b/swh/objstorage/api/server.py @@ -6,6 +6,7 @@ import os import aiohttp.web +import json from swh.core.config import read as config_read from swh.core.api.asynchronous import (RPCServerApp, decode_request, diff --git a/swh/objstorage/py.typed b/swh/objstorage/py.typed new file mode 100644 --- /dev/null +++ b/swh/objstorage/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/swh/objstorage/tests/test_objstorage_azure.py b/swh/objstorage/tests/test_objstorage_azure.py --- a/swh/objstorage/tests/test_objstorage_azure.py +++ b/swh/objstorage/tests/test_objstorage_azure.py @@ -7,6 +7,8 @@ from collections import defaultdict from unittest.mock import patch +from typing import Any, Dict + from azure.common import AzureMissingResourceHttpError from swh.model.hashutil import hash_to_hex from swh.objstorage import get_objstorage @@ -25,40 +27,40 @@ """Mock internal azure library which AzureCloudObjStorage depends upon. """ - data = {} + _data = {} # type: Dict[str, Any] def __init__(self, account_name, account_key, **kwargs): # do not care for the account_name and the api_secret_key here - self.data = defaultdict(dict) + self._data = defaultdict(dict) def get_container_properties(self, container_name): - self.data[container_name] - return container_name in self.data + self._data[container_name] + return container_name in self._data def create_blob_from_bytes(self, container_name, blob_name, blob): - self.data[container_name][blob_name] = blob + self._data[container_name][blob_name] = blob def get_blob_to_bytes(self, container_name, blob_name): - if blob_name not in self.data[container_name]: + if blob_name not in self._data[container_name]: raise AzureMissingResourceHttpError( 'Blob %s not found' % blob_name, 404) return MockBlob(name=blob_name, - content=self.data[container_name][blob_name]) + content=self._data[container_name][blob_name]) def delete_blob(self, container_name, blob_name): try: - self.data[container_name].pop(blob_name) + self._data[container_name].pop(blob_name) except KeyError: raise AzureMissingResourceHttpError( 'Blob %s not found' % blob_name, 404) return True def exists(self, container_name, blob_name): - return blob_name in self.data[container_name] + return blob_name in self._data[container_name] def list_blobs(self, container_name, marker=None, maxresults=None): - for blob_name, content in sorted(self.data[container_name].items()): + for blob_name, content in sorted(self._data[container_name].items()): if marker is None or blob_name > marker: yield MockBlob(name=blob_name, content=content) diff --git a/tox.ini b/tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=flake8,py3 +envlist=flake8,py3,mypy [testenv] deps = @@ -14,3 +14,11 @@ flake8 commands = {envpython} -m flake8 + +[testenv:mypy] +skip_install = true +deps = + .[testing] + mypy +commands = + mypy swh