diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ version.txt .tox .hypothesis +.mypy_cache/ diff --git a/mypy.ini b/mypy.ini new file mode 100644 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,36 @@ +[mypy] +namespace_packages = True +warn_unused_ignores = True + + +# 3rd party libraries without stubs (yet) + +[mypy-aiohttp_utils.*] +ignore_missing_imports = True + +[mypy-arrow.*] +ignore_missing_imports = True + +[mypy-celery.*] +ignore_missing_imports = True + +[mypy-decorator.*] +ignore_missing_imports = True + +[mypy-deprecated.*] +ignore_missing_imports = True + +[mypy-msgpack.*] +ignore_missing_imports = True + +[mypy-pkg_resources.*] +ignore_missing_imports = True + +[mypy-psycopg2.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True + +[mypy-systemd.*] +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/core/api/__init__.py b/swh/core/api/__init__.py --- a/swh/core/api/__init__.py +++ b/swh/core/api/__init__.py @@ -12,6 +12,8 @@ import requests import datetime +from typing import ClassVar, Optional, Type + from deprecated import deprecated from flask import Flask, Request, Response, request, abort from .serializers import (decode_response, @@ -140,7 +142,7 @@ """ - backend_class = None + backend_class = None # type: ClassVar[Optional[type]] """For each method of `backend_class` decorated with :func:`remote_api_endpoint`, a method with the same prototype and docstring will be added to this class. Calls to this new method will @@ -149,7 +151,7 @@ This backend class will never be instantiated, it only serves as a template.""" - api_exception = APIError + api_exception = APIError # type: ClassVar[Type[Exception]] """The exception class to raise in case of communication error with the server.""" diff --git a/swh/core/api/negotiation.py b/swh/core/api/negotiation.py --- a/swh/core/api/negotiation.py +++ b/swh/core/api/negotiation.py @@ -24,9 +24,10 @@ # from collections import defaultdict +from decorator import decorator from inspect import getcallargs -from decorator import decorator +from typing import Any, List, Optional class FormatterNotFound(Exception): @@ -34,8 +35,8 @@ class Formatter: - format = None - mimetypes = [] + format = None # type: Optional[str] + mimetypes = [] # type: List[Any] def __init__(self, request_mimetype=None): if request_mimetype is None or request_mimetype not in self.mimetypes: diff --git a/swh/core/api/tests/test_api.py b/swh/core/api/tests/test_api.py --- a/swh/core/api/tests/test_api.py +++ b/swh/core/api/tests/test_api.py @@ -5,7 +5,7 @@ import unittest -import requests_mock +import requests_mock # type: ignore from werkzeug.wrappers import BaseResponse from werkzeug.test import Client as WerkzeugTestClient diff --git a/swh/core/api/tests/test_async.py b/swh/core/api/tests/test_async.py --- a/swh/core/api/tests/test_async.py +++ b/swh/core/api/tests/test_async.py @@ -4,9 +4,8 @@ # See top-level LICENSE file for more information import datetime -import json - import msgpack +import json import pytest diff --git a/swh/core/config.py b/swh/core/config.py --- a/swh/core/config.py +++ b/swh/core/config.py @@ -10,6 +10,8 @@ from itertools import chain from copy import deepcopy +from typing import Any, Dict, Optional, Tuple + logger = logging.getLogger(__name__) @@ -311,8 +313,8 @@ """ - DEFAULT_CONFIG = {} - CONFIG_BASE_FILENAME = '' + DEFAULT_CONFIG = {} # type: Dict[str, Tuple[str, Any]] + CONFIG_BASE_FILENAME = '' # type: Optional[str] @classmethod def parse_config_file(cls, base_filename=None, config_filename=None, diff --git a/swh/core/db/tests/db_testing.py b/swh/core/db/tests/db_testing.py --- a/swh/core/db/tests/db_testing.py +++ b/swh/core/db/tests/db_testing.py @@ -9,9 +9,12 @@ import psycopg2 +from typing import Dict, Iterable, Optional, Tuple, Union + from swh.core.utils import numfile_sortkey as sortkey -DB_DUMP_TYPES = {'.sql': 'psql', '.dump': 'pg_dump'} + +DB_DUMP_TYPES = {'.sql': 'psql', '.dump': 'pg_dump'} # type: Dict[str, str] def swh_db_version(dbname_or_service): @@ -207,8 +210,8 @@ """ - _DB_DUMP_LIST = {} - _DB_LIST = {} + _DB_DUMP_LIST = {} # type: Dict[str, Iterable[Tuple[str, str]]] + _DB_LIST = {} # type: Dict[str, DbTestContext] DB_TEST_FIXTURE_IMPORTED = True @classmethod @@ -286,7 +289,7 @@ """ TEST_DB_NAME = 'softwareheritage-test' - TEST_DB_DUMP = None + TEST_DB_DUMP = None # type: Optional[Union[str, Iterable[str]]] @classmethod def setUpClass(cls): diff --git a/swh/core/py.typed b/swh/core/py.typed new file mode 100644 --- /dev/null +++ b/swh/core/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/tox.ini b/tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=flake8,py3-{core,db,server} +envlist=flake8,py3-{core,db,server},mypy [testenv:py3-core] deps = @@ -42,3 +42,12 @@ flake8 commands = {envpython} -m flake8 + +[testenv:mypy] +skip_install = true +deps = + .[testing] + mypy + django-stubs +commands = + mypy swh