Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement mypy annotations (Resolves #549) #557

Merged
merged 6 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
[mypy]
# TODO: enable more flags like in https://github.com/ocf/slackbridge/blob/master/mypy.ini
show_traceback = True
ignore_missing_imports = True
check_untyped_defs = True

plugins =
mypy_django_plugin.main

disallow_untyped_defs = True
disallow_untyped_calls = True
disallow_any_generics = True

warn_no_return = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unused_ignores = True

strict_optional = True
no_implicit_optional = True

# new_semantic_analyzer = True -> no longer an option in mypy 0.720+, enabled by default
64bitpandas marked this conversation as resolved.
Show resolved Hide resolved

[mypy.plugins.django-stubs]
django_settings_module = ocfweb.settings
6 changes: 4 additions & 2 deletions ocfweb/about/lab.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render


def lab_open_source(request):
def lab_open_source(request: HttpRequest) -> HttpResponse:
return render(
request,
'about/lab-open-source.html',
Expand All @@ -11,7 +13,7 @@ def lab_open_source(request):
)


def lab_vote(request):
def lab_vote(request: HttpRequest) -> HttpResponse:
return render(
request,
'about/lab-vote.html',
Expand Down
4 changes: 3 additions & 1 deletion ocfweb/about/staff.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render


def about_staff(request):
def about_staff(request: HttpRequest) -> HttpResponse:
return render(
request,
'about/staff.html',
Expand Down
25 changes: 16 additions & 9 deletions ocfweb/account/chpass.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from typing import Any
from typing import Iterator
from typing import List

from django import forms
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
Expand All @@ -15,15 +21,14 @@
from ocfweb.component.celery import change_password as change_password_task
from ocfweb.component.forms import Form


CALLINK_ERROR_MSG = (
"Couldn't connect to CalLink API. Resetting group "
'account passwords online is unavailable.'
)


def get_accounts_signatory_for(calnet_uid):
def flatten(lst):
def get_accounts_signatory_for(calnet_uid: str) -> List[Any]:
def flatten(lst: Iterator[Any]) -> List[Any]:
return [item for sublist in lst for item in sublist]

group_accounts = flatten(
Expand All @@ -40,7 +45,7 @@ def flatten(lst):
return group_accounts


def get_accounts_for(calnet_uid):
def get_accounts_for(calnet_uid: str) -> List[Any]:
accounts = users_by_calnet_uid(calnet_uid)

if calnet_uid in TESTER_CALNET_UIDS:
Expand All @@ -51,7 +56,7 @@ def get_accounts_for(calnet_uid):


@calnet_required
def change_password(request):
def change_password(request: HttpRequest) -> HttpResponse:
calnet_uid = request.session['calnet_uid']
error = None
accounts = get_accounts_for(calnet_uid)
Expand Down Expand Up @@ -118,14 +123,16 @@ def change_password(request):

class ChpassForm(Form):

def __init__(self, ocf_accounts, calnet_uid, *args, **kwargs):
def __init__(self, ocf_accounts: List[str], calnet_uid: str, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.calnet_uid = calnet_uid
self.fields['ocf_account'] = forms.ChoiceField(
choices=[(x, x) for x in ocf_accounts],
label='OCF account',
)
self.fields.keyOrder = [

# mypy expects fields to be a dict, but it isn't. This is defined in django so it can't be fixed
self.fields.keyOrder = [ # type: ignore
64bitpandas marked this conversation as resolved.
Show resolved Hide resolved
'ocf_account',
'new_password',
'confirm_password',
Expand All @@ -141,7 +148,7 @@ def __init__(self, ocf_accounts, calnet_uid, *args, **kwargs):
label='Confirm password',
)

def clean_ocf_account(self):
def clean_ocf_account(self) -> str:
data = self.cleaned_data['ocf_account']
if not user_exists(data):
raise forms.ValidationError('OCF user account does not exist.')
Expand All @@ -161,7 +168,7 @@ def clean_ocf_account(self):

return data

def clean_confirm_password(self):
def clean_confirm_password(self) -> str:
new_password = self.cleaned_data.get('new_password')
confirm_password = self.cleaned_data.get('confirm_password')

Expand Down
4 changes: 3 additions & 1 deletion ocfweb/account/commands.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django import forms
from django.forms import widgets
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render
from paramiko import AuthenticationException
from paramiko import SSHClient
Expand All @@ -8,7 +10,7 @@
from ocfweb.component.forms import Form


def commands(request):
def commands(request: HttpRequest) -> HttpResponse:
command_to_run = ''
output = ''
error = ''
Expand Down
10 changes: 6 additions & 4 deletions ocfweb/account/recommender.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from random import randint
from typing import Any
from typing import List

from ocflib.account.creation import validate_username
from ocflib.account.creation import ValidationError
from ocflib.account.creation import ValidationWarning


def recommend(real_name, n):
name_fields = [name.lower() for name in real_name.split()]
def recommend(real_name: str, n: int) -> List[Any]:
name_fields: List[str] = [name.lower() for name in real_name.split()]

# Can reimplement name_field_abbrevs to only remove vowels or consonants
name_field_abbrevs = [[] for i in range(len(name_fields))]
name_field_abbrevs: List[List[str]] = [[] for i in range(len(name_fields))]
for i in range(len(name_fields)):
name_field = name_fields[i]
for j in range(1, len(name_field) + 1):
Expand All @@ -23,7 +25,7 @@ def recommend(real_name, n):
new_unvalidated_recs.append(rec + name_field_abbrev)
unvalidated_recs = new_unvalidated_recs

validated_recs = []
validated_recs: List[Any] = []
while len(validated_recs) < n and len(unvalidated_recs) > 0:
rec = unvalidated_recs.pop(randint(0, len(unvalidated_recs) - 1))
try:
Expand Down
26 changes: 16 additions & 10 deletions ocfweb/account/register.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from typing import Any
from typing import Union

import ocflib.account.search as search
import ocflib.account.validators as validators
import ocflib.misc.validators
import ocflib.ucb.directory as directory
from Crypto.PublicKey import RSA
from django import forms
from django.core.exceptions import NON_FIELD_ERRORS
from django.http import HttpRequest
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.http import HttpResponseRedirect
from django.http import JsonResponse
Expand All @@ -29,7 +34,7 @@


@calnet_required
def request_account(request):
def request_account(request: HttpRequest) -> Union[HttpResponseRedirect, HttpResponse]:
calnet_uid = request.session['calnet_uid']
status = 'new_request'

Expand Down Expand Up @@ -61,7 +66,7 @@ def request_account(request):
real_name = directory.name_by_calnet_uid(calnet_uid)

if request.method == 'POST':
form = ApproveForm(request.POST)
form: Any = ApproveForm(request.POST)
emmatyping marked this conversation as resolved.
Show resolved Hide resolved
if form.is_valid():
req = NewAccountRequest(
user_name=form.cleaned_data['ocf_login_name'],
Expand Down Expand Up @@ -114,7 +119,7 @@ def request_account(request):
)


def recommend(request):
def recommend(request: HttpRequest) -> Union[JsonResponse, HttpResponseBadRequest]:
real_name = request.GET.get('real_name', None)
if real_name is None:
return HttpResponseBadRequest('No real_name in recommend request')
Expand All @@ -127,7 +132,7 @@ def recommend(request):
)


def validate(request):
def validate(request: HttpRequest) -> Union[HttpResponseBadRequest, JsonResponse]:
real_name = request.GET.get('real_name', None)
if real_name is None:
return HttpResponseBadRequest('No real_name in validate request')
Expand All @@ -149,7 +154,7 @@ def validate(request):
})


def wait_for_account(request):
def wait_for_account(request: HttpRequest) -> Union[HttpResponse, HttpResponseRedirect]:
if 'approve_task_id' not in request.session:
return render(
request,
Expand Down Expand Up @@ -180,11 +185,11 @@ def wait_for_account(request):
return render(request, 'account/register/wait/error-probably-not-created.html', {})


def account_pending(request):
def account_pending(request: HttpRequest) -> HttpResponse:
return render(request, 'account/register/pending.html', {'title': 'Account request pending'})


def account_created(request):
def account_created(request: HttpRequest) -> HttpResponse:
return render(request, 'account/register/success.html', {'title': 'Account request successful'})


Expand Down Expand Up @@ -232,7 +237,7 @@ class ApproveForm(Form):
},
)

def clean_verify_password(self):
def clean_verify_password(self) -> str:
password = self.cleaned_data.get('password')
verify_password = self.cleaned_data.get('verify_password')

Expand All @@ -241,7 +246,7 @@ def clean_verify_password(self):
raise forms.ValidationError("Your passwords don't match.")
return verify_password

def clean_verify_contact_email(self):
def clean_verify_contact_email(self) -> str:
email = self.cleaned_data.get('contact_email')
verify_contact_email = self.cleaned_data.get('verify_contact_email')

Expand All @@ -250,7 +255,8 @@ def clean_verify_contact_email(self):
raise forms.ValidationError("Your emails don't match.")
return verify_contact_email

def clean(self):
# clean incompatible with supertype BaseForm which is defined in django.
def clean(self) -> None: # type: ignore
cleaned_data = super().clean()

# validate password (requires username to check similarity)
Expand Down
4 changes: 3 additions & 1 deletion ocfweb/account/templatetags/vhost_mail.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from typing import List

from django import template

register = template.Library()


@register.filter
def address_to_parts(address):
def address_to_parts(address: str) -> List[str]:
return address.split('@')
21 changes: 13 additions & 8 deletions ocfweb/account/vhost.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import re
import socket
from textwrap import dedent
from typing import Any

from django import forms
from django.conf import settings
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
Expand All @@ -23,18 +26,18 @@
from ocfweb.component.session import logged_in_user


def available_domain(domain):
def available_domain(domain: str) -> bool:
if not re.match(r'^[a-zA-Z0-9]+\.berkeley\.edu$', domain):
return False
return not host_exists(domain)


def valid_domain_external(domain):
def valid_domain_external(domain: str) -> bool:
return bool(re.match(r'([a-zA-Z0-9]+\.)+[a-zA-Z0-9]{2,}', domain))


@login_required
def request_vhost(request):
def request_vhost(request: HttpRequest) -> HttpResponse:
user = logged_in_user(request)
attrs = user_attrs(user)
is_group = 'callinkOid' in attrs
Expand Down Expand Up @@ -137,7 +140,9 @@ def request_vhost(request):
else:
return redirect(reverse('request_vhost_success'))
else:
form = VirtualHostForm(is_group, initial={'requested_subdomain': user + '.berkeley.edu'})
# Unsupported left operand type for + ("None") because form might not have been instantiated at this point...
# but this doesn't matter because of if-else clause
form = VirtualHostForm(is_group, initial={'requested_subdomain': user + '.berkeley.edu'}) # type: ignore

group_url = f'https://www.ocf.berkeley.edu/~{user}/'

Expand All @@ -156,7 +161,7 @@ def request_vhost(request):
)


def request_vhost_success(request):
def request_vhost_success(request: HttpRequest) -> HttpResponse:
return render(
request,
'account/vhost/success.html',
Expand Down Expand Up @@ -231,7 +236,7 @@ class VirtualHostForm(Form):
max_length=1024,
)

def __init__(self, is_group=True, *args, **kwargs):
def __init__(self, is_group: bool = True, *args: Any, **kwargs: Any) -> None:
super(Form, self).__init__(*args, **kwargs)

# It's pretty derpy that we have to set the labels here, but we can't
Expand Down Expand Up @@ -266,7 +271,7 @@ def __init__(self, is_group=True, *args, **kwargs):
max_length=64,
)

def clean_requested_subdomain(self):
def clean_requested_subdomain(self) -> str:
requested_subdomain = self.cleaned_data['requested_subdomain'].lower().strip()

if self.cleaned_data['requested_own_domain']:
Expand All @@ -291,7 +296,7 @@ def clean_requested_subdomain(self):

return requested_subdomain

def clean_your_email(self):
def clean_your_email(self) -> str:
your_email = self.cleaned_data['your_email']
if not valid_email(your_email):
raise forms.ValidationError(
Expand Down
Loading