diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ *.tar.bz2 *.tar.lzma .tox/ +.mypy_cache/ diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,3 +9,4 @@ recursive-include swh/deposit/fixtures * recursive-include swh/deposit/templates * recursive-include swh/deposit/tests/*/data * +recursive-include swh py.typed diff --git a/Makefile.local b/Makefile.local --- a/Makefile.local +++ b/Makefile.local @@ -25,3 +25,10 @@ run: gunicorn3 -b 127.0.0.1:5006 swh.deposit.wsgi + +# Override default rule to make sure DJANGO env var is properly set. It +# *should* work without any override thanks to the mypy django-stubs plugin, +# but it currently doesn't; see +# https://github.com/typeddjango/django-stubs/issues/166 +typecheck: + DJANGO_SETTINGS_MODULE=swh.deposit.settings.testing $(MYPY) $(MYPYFLAGS) swh diff --git a/mypy.ini b/mypy.ini new file mode 100644 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,34 @@ +[mypy] +namespace_packages = True +warn_unused_ignores = True + + +# support for django magic: https://github.com/typeddjango/django-stubs +plugins = mypy_django_plugin.main + +[mypy.plugins.django-stubs] +django_settings_module = swh.deposit.settings.testing + + +# 3rd party libraries without stubs (yet) + +[mypy-celery.*] +ignore_missing_imports = True + +[mypy-iso8601.*] +ignore_missing_imports = True + +[mypy-pkg_resources.*] +ignore_missing_imports = True + +[mypy-psycopg2.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True + +[mypy-rest_framework.*] +ignore_missing_imports = True + +[mypy-xmltodict.*] +ignore_missing_imports = True diff --git a/requirements-test.txt b/requirements-test.txt --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,3 +3,4 @@ swh.scheduler[testing] pytest-postgresql >= 2.1.0 requests_mock +django-stubs 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 pkgutil import extend_path +from typing import Iterable + +__path__ = extend_path(__path__, __name__) # type: Iterable[str] diff --git a/swh/deposit/api/common.py b/swh/deposit/api/common.py --- a/swh/deposit/api/common.py +++ b/swh/deposit/api/common.py @@ -5,6 +5,8 @@ import hashlib +from typing import Any, Tuple + from abc import ABCMeta, abstractmethod from django.urls import reverse from django.http import HttpResponse @@ -46,7 +48,7 @@ authentication check """ - authentication_classes = (BasicAuthentication, ) + authentication_classes = (BasicAuthentication, ) # type: Tuple[Any, ...] permission_classes = (IsAuthenticated, ) diff --git a/swh/deposit/api/private/__init__.py b/swh/deposit/api/private/__init__.py --- a/swh/deposit/api/private/__init__.py +++ b/swh/deposit/api/private/__init__.py @@ -85,8 +85,10 @@ return {'headers': headers} - def get(self, req, collection_name=None, deposit_id=None, format=None): + def get(self, req, collection_name=None, deposit_id=None, format=None, + *args, **kwargs): return super().get(req, collection_name, deposit_id, format) - def put(self, req, collection_name=None, deposit_id=None, format=None): + def put(self, req, collection_name=None, deposit_id=None, format=None, + *args, **kwargs): return super().put(req, collection_name, deposit_id, format) diff --git a/swh/deposit/config.py b/swh/deposit/config.py --- a/swh/deposit/config.py +++ b/swh/deposit/config.py @@ -6,6 +6,8 @@ import os import logging +from typing import Any, Dict, Tuple + from swh.core.config import SWHConfig from swh.scheduler import get_scheduler @@ -97,7 +99,7 @@ }) } - ADDITIONAL_CONFIG = {} + ADDITIONAL_CONFIG = {} # type: Dict[str, Tuple[str, Any]] def __init__(self, **config): super().__init__() diff --git a/swh/deposit/models.py b/swh/deposit/models.py --- a/swh/deposit/models.py +++ b/swh/deposit/models.py @@ -76,7 +76,10 @@ """ collections = ArrayField(models.IntegerField(), null=True) - objects = UserManager() + objects = UserManager() # type: ignore + # this typing hint is due to a mypy/django-stubs limitation, + # see https://github.com/typeddjango/django-stubs/issues/174 + provider_url = models.TextField(null=False) domain = models.TextField(null=False) diff --git a/swh/deposit/py.typed b/swh/deposit/py.typed new file mode 100644 --- /dev/null +++ b/swh/deposit/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. diff --git a/swh/deposit/tests/__init__.py b/swh/deposit/tests/__init__.py --- a/swh/deposit/tests/__init__.py +++ b/swh/deposit/tests/__init__.py @@ -67,7 +67,7 @@ # monkey patch classes method permits to override, for tests purposes, # the default configuration without side-effect, i.e do not load the # configuration from disk -SWHDefaultConfig.parse_config_file = parse_deposit_config_file -BufferedLoader.parse_config_file = parse_loader_config_file +SWHDefaultConfig.parse_config_file = parse_deposit_config_file # type: ignore +BufferedLoader.parse_config_file = parse_loader_config_file # type: ignore setup_django_for('testing') diff --git a/swh/deposit/tests/api/test_deposit_delete.py b/swh/deposit/tests/api/test_deposit_delete.py --- a/swh/deposit/tests/api/test_deposit_delete.py +++ b/swh/deposit/tests/api/test_deposit_delete.py @@ -6,7 +6,7 @@ from collections import defaultdict from django.urls import reverse from rest_framework import status -from typing import Mapping +from typing import Dict, Mapping from swh.deposit.config import ( EDIT_SE_IRI, EM_IRI, ARCHIVE_KEY, METADATA_KEY, @@ -17,7 +17,7 @@ def count_deposit_request_types(deposit_requests) -> Mapping[str, int]: - deposit_request_types = defaultdict(int) + deposit_request_types = defaultdict(int) # type: Dict[str, int] for dr in deposit_requests: deposit_request_types[dr.type] += 1 return deposit_request_types diff --git a/swh/deposit/tests/conftest.py b/swh/deposit/tests/conftest.py --- a/swh/deposit/tests/conftest.py +++ b/swh/deposit/tests/conftest.py @@ -50,7 +50,7 @@ Used to initialize the deposit's server db. """ - import project.app.signals + import project.app.signals # type: ignore def prepare_db(*args, **kwargs): from django.conf import settings diff --git a/swh/deposit/tests/loader/conftest.py b/swh/deposit/tests/loader/conftest.py --- a/swh/deposit/tests/loader/conftest.py +++ b/swh/deposit/tests/loader/conftest.py @@ -17,7 +17,7 @@ from swh.deposit.loader.loader import DepositLoader -@pytest.fixture(scope='session') +@pytest.fixture(scope='session') # type: ignore # expected redefinition def celery_includes(): return [ 'swh.deposit.loader.tasks', diff --git a/tox.ini b/tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=flake8,py3 +envlist=flake8,mypy,py3 [testenv:py3] deps = @@ -29,3 +29,12 @@ commands = {envpython} -m flake8 \ --exclude=.tox,.git,__pycache__,.tox,.eggs,*.egg,swh/deposit/migrations + +[testenv:mypy] +setenv = DJANGO_SETTINGS_MODULE = swh.deposit.settings.testing +skip_install = true +deps = + .[testing] + mypy +commands = + mypy swh