Skip to content

Commit

Permalink
chore: update typehints via __future__.annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonGrace2282 committed Jun 18, 2024
1 parent 1d1ff53 commit 2bf69c6
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 109 deletions.
5 changes: 3 additions & 2 deletions intranet/apps/eighth/forms/admin/activities.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from typing import List # noqa

from django import forms, http
from django.contrib.auth import get_user_model
Expand All @@ -11,7 +12,7 @@


class ActivityDisplayField(forms.ModelChoiceField):
cancelled_acts = None # type: List[EighthActivity]
cancelled_acts: list[EighthActivity] | None = None

def __init__(self, *args, **kwargs):
if "block" in kwargs:
Expand Down
66 changes: 33 additions & 33 deletions intranet/apps/eighth/models.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions intranet/apps/eighth/tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import calendar
import datetime
from typing import Collection
Expand Down
9 changes: 5 additions & 4 deletions intranet/apps/eighth/views/admin/groups.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

import csv
import logging
import re
from typing import List, Optional

from cacheops import invalidate_model, invalidate_obj
from formtools.wizard.views import SessionWizardView
Expand Down Expand Up @@ -156,7 +157,7 @@ def get_file_string(fileobj):
return filetext


def get_user_info(key: str, val) -> Optional[List[User]]:
def get_user_info(key: str, val) -> list[User] | None:
if key in ["username", "id"]:
try:
u = get_user_model().objects.filter(**{key: val})
Expand Down Expand Up @@ -200,7 +201,7 @@ def handle_group_input(filetext: str):
return find_users_input(lines)


def find_users_input(lines: List[str]):
def find_users_input(lines: list[str]):
sure_users = []
unsure_users = []
for line in lines:
Expand Down Expand Up @@ -487,7 +488,7 @@ def eighth_admin_signup_group_action(request, group_id, schact_id):
)


def eighth_admin_perform_group_signup(*, group_id: int, schact_id: int, request: Optional[http.HttpRequest], skip_users: set):
def eighth_admin_perform_group_signup(*, group_id: int, schact_id: int, request: http.HttpRequest | None, skip_users: set):
"""Performs sign up of all users in a specific group up for a
specific scheduled activity.
Expand Down
5 changes: 3 additions & 2 deletions intranet/apps/eighth/views/admin/hybrid.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import logging
from typing import Optional

from formtools.wizard.views import SessionWizardView

Expand Down Expand Up @@ -215,7 +216,7 @@ def eighth_admin_signup_group_action_hybrid(request, group_id, schact_virtual_id
)


def eighth_admin_perform_group_signup(*, group_id: int, schact_virtual_id: int, schact_person_id: int, request: Optional[http.HttpRequest]):
def eighth_admin_perform_group_signup(*, group_id: int, schact_virtual_id: int, schact_person_id: int, request: http.HttpRequest | None):
"""Performs sign up of all users in a specific group up for a
specific scheduled activity.
Expand Down
4 changes: 2 additions & 2 deletions intranet/apps/features/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional
from __future__ import annotations


def get_feature_context(request) -> Optional[str]:
def get_feature_context(request) -> str | None:
"""Given a Django request, returns the 'context' that should be used to select feature
announcements to display (one of ``dashboard``, ``login``, ``eighth_signup``, or ``None``).
Expand Down
10 changes: 6 additions & 4 deletions intranet/apps/notifications/emails.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import logging
from typing import Collection, Mapping
from typing import Mapping, MutableSequence

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
Expand All @@ -13,11 +15,11 @@ def email_send(
html_template: str,
data: Mapping[str, object],
subject: str,
emails: Collection[str], # pylint: disable=unsubscriptable-object
headers: Mapping[str, str] = None, # pylint: disable=unsubscriptable-object
emails: MutableSequence[str],
headers: Mapping[str, str] | None = None,
bcc: bool = False,
*,
custom_logger: logging.Logger = None,
custom_logger: logging.Logger | None = None,
) -> EmailMultiAlternatives:
"""Send an HTML/Plaintext email with the following fields.
If we are not in production and settings.FORCE_EMAIL_SEND is not set, does not actually send the email
Expand Down
16 changes: 9 additions & 7 deletions intranet/apps/printing/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from __future__ import annotations

import logging
import math
import os
import re
import subprocess
import tempfile
from io import BytesIO
from typing import Dict, Optional

import magic
from sentry_sdk import add_breadcrumb, capture_exception
Expand All @@ -32,14 +33,15 @@ class InvalidInputPrintingError(Exception):
"""An error occurred while printing, but it was due to invalid input from the user and is not worthy of a ``CRITICAL`` log message."""


def get_printers() -> Dict[str, str]:
def get_printers() -> dict[str, str] | list:
"""Returns a dictionary mapping name:description for available printers.
This requires that a CUPS client be configured on the server.
Otherwise, this returns an empty dictionary.
Returns:
A dictionary mapping name:description for available printers.
A dictionary mapping name:description for available printers, or
an empty list if cups isn't installed or lpstat fails
"""

key = "printing:printers"
Expand Down Expand Up @@ -86,7 +88,7 @@ def get_printers() -> Dict[str, str]:
return printers


def convert_soffice(tmpfile_name: str) -> Optional[str]:
def convert_soffice(tmpfile_name: str) -> str | None:
"""Converts a doc or docx to a PDF with soffice.
Args:
Expand Down Expand Up @@ -117,7 +119,7 @@ def convert_soffice(tmpfile_name: str) -> Optional[str]:
return None


def convert_pdf(tmpfile_name: str, cmdname: str = "ps2pdf") -> Optional[str]:
def convert_pdf(tmpfile_name: str, cmdname: str = "ps2pdf") -> str | None:
new_name = f"{tmpfile_name}.pdf"
try:
output = subprocess.check_output([cmdname, tmpfile_name, new_name], stderr=subprocess.STDOUT, universal_newlines=True)
Expand Down Expand Up @@ -179,7 +181,7 @@ def get_mimetype(tmpfile_name: str) -> str:
return mimetype


def convert_file(tmpfile_name: str, orig_fname: str) -> Optional[str]:
def convert_file(tmpfile_name: str, orig_fname: str) -> str | None:
detected = get_mimetype(tmpfile_name)

add_breadcrumb(category="printing", message=f"Detected file type {detected}", level="debug")
Expand Down Expand Up @@ -211,7 +213,7 @@ def convert_file(tmpfile_name: str, orig_fname: str) -> Optional[str]:
raise InvalidInputPrintingError(f"Invalid file type {detected}")


def check_page_range(page_range: str, max_pages: int) -> Optional[int]:
def check_page_range(page_range: str, max_pages: int) -> int | None:
"""Returns the number of pages included in the range, or None if it is an invalid range.
Args:
Expand Down
5 changes: 3 additions & 2 deletions intranet/apps/signage/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import datetime
import logging
from typing import Optional

from django import http
from django.conf import settings
Expand All @@ -20,7 +21,7 @@
logger = logging.getLogger(__name__)


def check_internal_ip(request) -> Optional[HttpResponse]:
def check_internal_ip(request) -> HttpResponse | None:
"""
A method to determine if a request is allowed to load a signage page.
Expand Down
6 changes: 3 additions & 3 deletions intranet/apps/templatetags/paginate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Union
from __future__ import annotations

from django import template

Expand All @@ -13,8 +13,8 @@ def query_transform(request, **kwargs):
return query.urlencode()


@register.filter # TODO: replace return type with list[int | None]
def page_list(paginator, current_page) -> List[Union[int, None]]:
@register.filter
def page_list(paginator, current_page) -> list[int | None]:
"""Pagination
If there is a ``None`` in the output, it should be replaced
Expand Down
42 changes: 22 additions & 20 deletions intranet/apps/users/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# pylint: disable=too-many-lines; Allow more than 1000 lines
from __future__ import annotations

import logging
from base64 import b64encode
from datetime import timedelta
from typing import Collection, Dict, Optional, Union
from typing import Collection

from dateutil.relativedelta import relativedelta

Expand Down Expand Up @@ -42,14 +44,14 @@ class UserManager(DjangoUserManager):
"""

def user_with_student_id(self, student_id: Union[int, str]) -> Optional["User"]:
def user_with_student_id(self, student_id: int | str) -> "User | None":
"""Get a unique user object by FCPS student ID. (Ex. 1624472)"""
results = User.objects.filter(student_id=str(student_id))
if len(results) == 1:
return results.first()
return None

def user_with_ion_id(self, student_id: Union[int, str]) -> Optional["User"]:
def user_with_ion_id(self, student_id: int | str) -> "User | None":
"""Get a unique user object by Ion ID. (Ex. 489)"""
if isinstance(student_id, str) and not is_entirely_digit(student_id):
return None
Expand All @@ -58,11 +60,11 @@ def user_with_ion_id(self, student_id: Union[int, str]) -> Optional["User"]:
return results.first()
return None

def users_in_year(self, year: int) -> Union[Collection["User"], QuerySet]: # pylint: disable=unsubscriptable-object
def users_in_year(self, year: int) -> Collection["User"] | QuerySet: # pylint: disable=unsubscriptable-object
"""Get a list of users in a specific graduation year."""
return User.objects.filter(graduation_year=year)

def user_with_name(self, given_name: Optional[str] = None, last_name: Optional[str] = None) -> "User": # pylint: disable=unsubscriptable-object
def user_with_name(self, given_name: str | None = None, last_name: str | None = None) -> "User": # pylint: disable=unsubscriptable-object
"""Get a unique user object by given name (first/nickname) and/or last name.
Args:
Expand All @@ -86,14 +88,14 @@ def user_with_name(self, given_name: Optional[str] = None, last_name: Optional[s
except (User.DoesNotExist, User.MultipleObjectsReturned):
return None

def get_students(self) -> Union[Collection["User"], QuerySet]: # pylint: disable=unsubscriptable-object
def get_students(self) -> Collection["User"] | QuerySet: # pylint: disable=unsubscriptable-object
"""Get user objects that are students (quickly)."""
users = User.objects.filter(user_type="student", graduation_year__gte=get_senior_graduation_year())
users = users.exclude(id__in=EXTRA)

return users

def get_teachers(self) -> Union[Collection["User"], QuerySet]: # pylint: disable=unsubscriptable-object
def get_teachers(self) -> Collection["User"] | QuerySet: # pylint: disable=unsubscriptable-object
"""Get user objects that are teachers (quickly)."""
users = User.objects.filter(user_type="teacher")
users = users.exclude(id__in=EXTRA)
Expand Down Expand Up @@ -121,7 +123,7 @@ def get_teachers_attendance_users(self) -> "QuerySet[User]": # noqa

return users

def get_teachers_sorted(self) -> Union[Collection["User"], QuerySet]: # pylint: disable=unsubscriptable-object
def get_teachers_sorted(self) -> Collection["User"] | QuerySet: # pylint: disable=unsubscriptable-object
"""Returns a ``QuerySet`` of teachers sorted by last name, then first name.
Returns:
Expand Down Expand Up @@ -171,8 +173,8 @@ def get_approve_announcements_users_sorted(self) -> "QuerySet[User]": # noqa

def exclude_from_search(
self,
existing_queryset: Optional[Union[Collection["User"], QuerySet]] = None, # pylint: disable=unsubscriptable-object
) -> Union[Collection["User"], QuerySet]: # pylint: disable=unsubscriptable-object
existing_queryset: Collection["User"] | QuerySet | None = None, # pylint: disable=unsubscriptable-object
) -> Collection["User"] | QuerySet: # pylint: disable=unsubscriptable-object
if existing_queryset is None:
existing_queryset = self

Expand Down Expand Up @@ -260,7 +262,7 @@ def get_signage_user() -> "User":
return User(id=99999)

@property
def address(self) -> Optional["Address"]:
def address(self) -> "Address" | None:
"""Returns the ``Address`` object representing this user's address, or ``None`` if it is not
set or the current user does not have permission to access it.
Expand All @@ -272,7 +274,7 @@ def address(self) -> Optional["Address"]:
return self.properties.address

@property
def schedule(self) -> Optional[Union[QuerySet, Collection["Section"]]]: # pylint: disable=unsubscriptable-object
def schedule(self) -> QuerySet | Collection["Section"] | None: # pylint: disable=unsubscriptable-object
"""Returns a QuerySet of the ``Section`` objects representing the classes this student is
in, or ``None`` if the current user does not have permission to list this student's classes.
Expand All @@ -284,7 +286,7 @@ def schedule(self) -> Optional[Union[QuerySet, Collection["Section"]]]: # pylin
"""
return self.properties.schedule

def member_of(self, group: Union[Group, str]) -> bool:
def member_of(self, group: Group | str) -> bool:
"""Returns whether a user is a member of a certain group.
Args:
Expand Down Expand Up @@ -402,7 +404,7 @@ def get_short_name(self) -> str:
return self.short_name

@property
def primary_email_address(self) -> Optional[str]:
def primary_email_address(self) -> str | None:
try:
return self.primary_email.address if self.primary_email else None
except Email.DoesNotExist:
Expand Down Expand Up @@ -434,7 +436,7 @@ def tj_email(self) -> str:
return "{}@{}".format(self.username, domain)

@property
def non_tj_email(self) -> Optional[str]:
def non_tj_email(self) -> str | None:
"""
Returns the user's first non-TJ email found, or None if none is found.
Expand Down Expand Up @@ -476,7 +478,7 @@ def notification_email(self) -> str:
return email.address if email and email.address else self.tj_email

@property
def default_photo(self) -> Optional[bytes]:
def default_photo(self) -> bytes | None:
"""Returns default photo (in binary) that should be used
Returns:
Expand Down Expand Up @@ -511,7 +513,7 @@ def grade(self) -> "Grade":
return Grade(self.graduation_year)

@property
def permissions(self) -> Dict[str, bool]:
def permissions(self) -> dict[str, bool]:
"""Dynamically generate dictionary of privacy options.
Returns:
Expand Down Expand Up @@ -753,7 +755,7 @@ def is_global_admin(self) -> bool:

return self.member_of("admin_all") and self.is_staff and self.is_superuser

def can_manage_group(self, group: Union[Group, str]) -> bool:
def can_manage_group(self, group: Group | str) -> bool:
"""Checks whether this user has permission to edit/manage the given group (either
a Group or a group name).
Expand Down Expand Up @@ -1381,7 +1383,7 @@ def __getattr__(self, name):
raise AttributeError("{!r} object has no attribute {!r}".format(type(self).__name__, name))

@cached_property
def base64(self) -> Optional[bytes]:
def base64(self) -> bytes | None:
"""Returns base64 encoded binary data for a user's picture.
Returns:
Expand Down Expand Up @@ -1447,7 +1449,7 @@ def text(self) -> str:
return self._name

@staticmethod
def number_from_name(name: str) -> Optional[int]:
def number_from_name(name: str) -> int | None:
if name in Grade.names:
return Grade.names.index(name) + 9
return None
Expand Down
Loading

0 comments on commit 2bf69c6

Please sign in to comment.