Add typing (#3370)

Add typing
This commit is contained in:
Oskar Hahn 2017-08-23 20:51:06 +02:00 committed by GitHub
parent c4fe50b38d
commit 9d1ebac86e
17 changed files with 49 additions and 26 deletions

3
.gitignore vendored
View File

@ -31,3 +31,6 @@ tests/file/*
# Plugin development # Plugin development
openslides_* openslides_*
# Mypy cache for typechecking
.mypy_cache

View File

@ -21,7 +21,8 @@ install:
- node_modules/.bin/gulp --production - node_modules/.bin/gulp --production
script: script:
- flake8 openslides tests - flake8 openslides tests
- isort --check-only --recursive openslides tests - if [ "`python --version`" \> "Python 3.5.0" ]; then isort --check-only --recursive openslides tests; fi
- python -m mypy openslides/
- node_modules/.bin/gulp jshint - node_modules/.bin/gulp jshint
- node_modules/.bin/karma start --browsers PhantomJS tests/karma/karma.conf.js - node_modules/.bin/karma start --browsers PhantomJS tests/karma/karma.conf.js

View File

@ -399,7 +399,9 @@ class AssignmentOption(RESTModelMixin, BaseOption):
return self.poll.assignment return self.poll.assignment
class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin, # TODO: remove the type-ignoring in the next line, after this is solved:
# https://github.com/python/mypy/issues/3855
class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin, # type: ignore
PublishPollMixin, BasePoll): PublishPollMixin, BasePoll):
option_class = AssignmentOption option_class = AssignmentOption

View File

@ -994,7 +994,9 @@ class MotionOption(RESTModelMixin, BaseOption):
return self.poll.motion return self.poll.motion
class MotionPoll(RESTModelMixin, CollectDefaultVotesMixin, BasePoll): # TODO: remove the type-ignoring in the next line, after this is solved:
# https://github.com/python/mypy/issues/3855
class MotionPoll(RESTModelMixin, CollectDefaultVotesMixin, BasePoll): # type: ignore
"""The Class to saves the vote result for a motion poll.""" """The Class to saves the vote result for a motion poll."""
motion = models.ForeignKey( motion = models.ForeignKey(

View File

@ -1,4 +1,5 @@
import locale import locale
from typing import Type # noqa
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
@ -16,7 +17,7 @@ class BaseOption(models.Model):
which has to be a subclass of BaseVote. Otherwise you have to override the which has to be a subclass of BaseVote. Otherwise you have to override the
get_vote_class method. get_vote_class method.
""" """
vote_class = None vote_class = None # type: Type[BaseVote]
class Meta: class Meta:
abstract = True abstract = True

View File

@ -2,10 +2,10 @@ from random import choice
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import Group as DjangoGroup from django.contrib.auth.models import Group as DjangoGroup
from django.contrib.auth.models import GroupManager as _GroupManager
from django.contrib.auth.models import ( from django.contrib.auth.models import (
AbstractBaseUser, AbstractBaseUser,
BaseUserManager, BaseUserManager,
GroupManager,
Permission, Permission,
PermissionsMixin, PermissionsMixin,
) )
@ -229,7 +229,7 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
raise RuntimeError('Do not use user.has_perm() but use openslides.utils.auth.has_perm') raise RuntimeError('Do not use user.has_perm() but use openslides.utils.auth.has_perm')
class GroupManager(GroupManager): class GroupManager(_GroupManager):
""" """
Customized manager that supports our get_full_queryset method. Customized manager that supports our get_full_queryset method.
""" """

View File

@ -1,10 +1,13 @@
from typing import Optional, Union
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.db.models import Model
from .collection import CollectionElement from .collection import CollectionElement
def has_perm(user, perm): def has_perm(user: Optional[CollectionElement], perm: str) -> bool:
""" """
Checks that user has a specific permission. Checks that user has a specific permission.
@ -34,7 +37,7 @@ def has_perm(user, perm):
return has_perm return has_perm
def anonymous_is_enabled(): def anonymous_is_enabled() -> bool:
""" """
Returns True if the anonymous user is enabled in the settings. Returns True if the anonymous user is enabled in the settings.
""" """
@ -42,7 +45,10 @@ def anonymous_is_enabled():
.get_full_data()['value']) .get_full_data()['value'])
def user_to_collection_user(user): AnyUser = Union[Model, CollectionElement, int, AnonymousUser, None]
def user_to_collection_user(user: AnyUser) -> Optional[CollectionElement]:
""" """
Takes an object, that represents a user and converts it to a CollectionElement Takes an object, that represents a user and converts it to a CollectionElement
or to None, if it is an anonymous user. or to None, if it is an anonymous user.

View File

@ -261,6 +261,6 @@ def get_redis_connection():
if use_redis_cache(): if use_redis_cache():
websocket_user_cache = RedisWebsocketUserCache() websocket_user_cache = RedisWebsocketUserCache() # type: BaseWebsocketUserCache
else: else:
websocket_user_cache = DjangoCacheWebsocketUserCache() websocket_user_cache = DjangoCacheWebsocketUserCache()

View File

@ -1,3 +1,5 @@
from typing import Mapping # noqa
from django.apps import apps from django.apps import apps
from django.core.cache import cache from django.core.cache import cache
@ -507,7 +509,7 @@ class Collection:
cache.set(self.get_cache_key(), ids) cache.set(self.get_cache_key(), ids)
_models_to_collection_string = {} _models_to_collection_string = {} # type: Mapping[str, object]
def get_model_from_collection_string(collection_string): def get_model_from_collection_string(collection_string):

View File

@ -71,7 +71,7 @@ class SignalConnectMetaClass(type):
return new_class return new_class
@classmethod @classmethod # type: ignore
def get_all(cls, request=None): def get_all(cls, request=None):
""" """
Collects all objects of the class created by the SignalConnectMetaClass Collects all objects of the class created by the SignalConnectMetaClass

View File

@ -1,5 +1,6 @@
from django.db import models from django.db import models
from .access_permissions import BaseAccessPermissions # noqa
from .utils import convert_camel_case_to_pseudo_snake_case from .utils import convert_camel_case_to_pseudo_snake_case
@ -23,7 +24,7 @@ class RESTModelMixin:
Mixin for Django models which are used in our REST API. Mixin for Django models which are used in our REST API.
""" """
access_permissions = None access_permissions = None # type: BaseAccessPermissions
def get_root_rest_element(self): def get_root_rest_element(self):
""" """

View File

@ -1,3 +1,5 @@
from typing import Optional # noqa
from django.dispatch import Signal from django.dispatch import Signal
from .collection import CollectionElement from .collection import CollectionElement
@ -14,7 +16,7 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
magic. magic.
""" """
signal = Signal() signal = Signal()
name = None name = None # type: Optional[str]
def __init__(self, **kwargs): def __init__(self, **kwargs):
""" """

View File

@ -1,4 +1,5 @@
from collections import OrderedDict from collections import OrderedDict
from typing import Optional # noqa
from django.http import Http404 from django.http import Http404
from rest_framework import status # noqa from rest_framework import status # noqa
@ -30,6 +31,7 @@ from rest_framework.viewsets import GenericViewSet as _GenericViewSet # noqa
from rest_framework.viewsets import ModelViewSet as _ModelViewSet # noqa from rest_framework.viewsets import ModelViewSet as _ModelViewSet # noqa
from rest_framework.viewsets import ViewSet as _ViewSet # noqa from rest_framework.viewsets import ViewSet as _ViewSet # noqa
from .access_permissions import BaseAccessPermissions # noqa
from .auth import user_to_collection_user from .auth import user_to_collection_user
from .collection import Collection, CollectionElement from .collection import Collection, CollectionElement
@ -102,7 +104,7 @@ class PermissionMixin:
Also connects container to handle access permissions for model and Also connects container to handle access permissions for model and
viewset. viewset.
""" """
access_permissions = None access_permissions = None # type: Optional[BaseAccessPermissions]
def get_permissions(self): def get_permissions(self):
""" """

View File

@ -1,3 +1,5 @@
from typing import List # noqa
from django.views import generic as django_views from django.views import generic as django_views
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from rest_framework.response import Response from rest_framework.response import Response
@ -22,7 +24,7 @@ class APIView(_APIView):
The Django Rest framework APIView with improvements for OpenSlides. The Django Rest framework APIView with improvements for OpenSlides.
""" """
http_method_names = [] http_method_names = [] # type: List[str]
""" """
The allowed actions have to be explicitly defined. The allowed actions have to be explicitly defined.

View File

@ -5,3 +5,4 @@
coverage coverage
flake8 flake8
isort==4.2.5 isort==4.2.5
mypy

View File

@ -13,3 +13,10 @@ max_line_length = 150
[isort] [isort]
include_trailing_comma = true include_trailing_comma = true
multi_line_output = 3 multi_line_output = 3
[mypy]
ignore_missing_imports = true
strict_optional = true
[mypy-openslides.utils.auth]
disallow_any = unannotated

View File

@ -1,8 +1,7 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch from unittest.mock import patch
from openslides.core.config import ConfigVariable, config from openslides.core.config import ConfigVariable
from openslides.core.exceptions import ConfigNotFound
class TestConfigVariable(TestCase): class TestConfigVariable(TestCase):
@ -23,11 +22,3 @@ class TestConfigVariable(TestCase):
'test_default_value', 'test_default_value',
"The value of config_variable.data['default_value'] should be the same " "The value of config_variable.data['default_value'] should be the same "
"as set as second argument of ConfigVariable()") "as set as second argument of ConfigVariable()")
class TestConfigHandler(TestCase):
def test_get_not_found(self):
self.assertRaises(
ConfigNotFound,
config.__getitem__,
'key_leehah4Sho4ee7aCohbn')