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
openslides_*
# Mypy cache for typechecking
.mypy_cache

View File

@ -21,7 +21,8 @@ install:
- node_modules/.bin/gulp --production
script:
- 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/karma start --browsers PhantomJS tests/karma/karma.conf.js

View File

@ -399,7 +399,9 @@ class AssignmentOption(RESTModelMixin, BaseOption):
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):
option_class = AssignmentOption

View File

@ -994,7 +994,9 @@ class MotionOption(RESTModelMixin, BaseOption):
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."""
motion = models.ForeignKey(

View File

@ -1,4 +1,5 @@
import locale
from typing import Type # noqa
from django.core.exceptions import ObjectDoesNotExist
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
get_vote_class method.
"""
vote_class = None
vote_class = None # type: Type[BaseVote]
class Meta:
abstract = True

View File

@ -2,10 +2,10 @@ from random import choice
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import Group as DjangoGroup
from django.contrib.auth.models import GroupManager as _GroupManager
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
GroupManager,
Permission,
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')
class GroupManager(GroupManager):
class GroupManager(_GroupManager):
"""
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.models import AnonymousUser
from django.db.models import Model
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.
@ -34,7 +37,7 @@ def has_perm(user, perm):
return has_perm
def anonymous_is_enabled():
def anonymous_is_enabled() -> bool:
"""
Returns True if the anonymous user is enabled in the settings.
"""
@ -42,7 +45,10 @@ def anonymous_is_enabled():
.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
or to None, if it is an anonymous user.

View File

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

View File

@ -1,3 +1,5 @@
from typing import Mapping # noqa
from django.apps import apps
from django.core.cache import cache
@ -507,7 +509,7 @@ class Collection:
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):

View File

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

View File

@ -1,5 +1,6 @@
from django.db import models
from .access_permissions import BaseAccessPermissions # noqa
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.
"""
access_permissions = None
access_permissions = None # type: BaseAccessPermissions
def get_root_rest_element(self):
"""

View File

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

View File

@ -1,4 +1,5 @@
from collections import OrderedDict
from typing import Optional # noqa
from django.http import Http404
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 ViewSet as _ViewSet # noqa
from .access_permissions import BaseAccessPermissions # noqa
from .auth import user_to_collection_user
from .collection import Collection, CollectionElement
@ -102,7 +104,7 @@ class PermissionMixin:
Also connects container to handle access permissions for model and
viewset.
"""
access_permissions = None
access_permissions = None # type: Optional[BaseAccessPermissions]
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.decorators.csrf import ensure_csrf_cookie
from rest_framework.response import Response
@ -22,7 +24,7 @@ class APIView(_APIView):
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.

View File

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

View File

@ -13,3 +13,10 @@ max_line_length = 150
[isort]
include_trailing_comma = true
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.mock import patch
from openslides.core.config import ConfigVariable, config
from openslides.core.exceptions import ConfigNotFound
from openslides.core.config import ConfigVariable
class TestConfigVariable(TestCase):
@ -23,11 +22,3 @@ class TestConfigVariable(TestCase):
'test_default_value',
"The value of config_variable.data['default_value'] should be the same "
"as set as second argument of ConfigVariable()")
class TestConfigHandler(TestCase):
def test_get_not_found(self):
self.assertRaises(
ConfigNotFound,
config.__getitem__,
'key_leehah4Sho4ee7aCohbn')