diff --git a/accounts/admin.py b/accounts/admin.py index 242d34d9c..ff5ab7ad7 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -46,7 +49,7 @@ def lookups(self, request, model_admin): course_identifiers = ( _filter_courses_for_user(Course.objects, request.user) .values_list("identifier", flat=True)) - return zip(course_identifiers, course_identifiers) + return zip(course_identifiers, course_identifiers, strict=True) def queryset(self, request, queryset): if self.value(): diff --git a/accounts/apps.py b/accounts/apps.py index 4ea1a53dd..9d8f903a3 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ diff --git a/accounts/models.py b/accounts/models.py index f11f733b4..469e8dc5a 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/accounts/utils.py b/accounts/utils.py index ce7331e45..d86b58853 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ @@ -143,7 +146,7 @@ def check_email_appellation_priority_list(self): if not custom_email_appellation_priority_list: return errors - if not isinstance(custom_email_appellation_priority_list, (list, tuple)): + if not isinstance(custom_email_appellation_priority_list, list | tuple): errors.append(Warning( msg=("{}, {}".format( INSTANCE_ERROR_PATTERN diff --git a/course/admin.py b/course/admin.py index 37d9f2e81..b97d05574 100644 --- a/course/admin.py +++ b/course/admin.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -20,7 +23,7 @@ THE SOFTWARE. """ -from typing import Any, Tuple +from typing import Any from django import forms from django.contrib import admin @@ -85,7 +88,7 @@ def _filter_participation_linked_obj_for_user(queryset, user): # {{{ list filter helper -def _filter_related_only(filter_arg: str) -> Tuple[str, Any]: +def _filter_related_only(filter_arg: str) -> tuple[str, Any]: return (filter_arg, admin.RelatedOnlyFieldListFilter) # }}} @@ -573,7 +576,7 @@ def lookups(self, request, model_admin): flow_session__course__participations__roles__permissions__permission=pperm.use_admin_interface) flow_ids = qs.values_list("flow_session__flow_id", flat=True).distinct() - return zip(flow_ids, flow_ids) + return zip(flow_ids, flow_ids, strict=True) def queryset(self, request, queryset): if self.value(): diff --git a/course/analytics.py b/course/analytics.py index dc0054f04..fb43c5d9a 100644 --- a/course/analytics.py +++ b/course/analytics.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -181,7 +184,7 @@ def get_bin_info_list(self): 100*weight/total_weight if total_weight else None)) - for start, weight in zip(num_bin_starts, bins)] + for start, weight in zip(num_bin_starts, bins, strict=True)] str_bin_info = [ BinInfo( diff --git a/course/apps.py b/course/apps.py index 45b94bfcc..1354cf59e 100644 --- a/course/apps.py +++ b/course/apps.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ diff --git a/course/calendar.py b/course/calendar.py index 19af28cc5..ba5a93967 100644 --- a/course/calendar.py +++ b/course/calendar.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/course/constants.py b/course/constants.py index 0cde3b064..7c25b7bfc 100644 --- a/course/constants.py +++ b/course/constants.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -20,7 +23,6 @@ THE SOFTWARE. """ -import typing from django.utils.translation import gettext, pgettext_lazy @@ -296,7 +298,7 @@ class flow_session_expiration_mode: # noqa def is_expiration_mode_allowed( - expmode: str, permissions: typing.FrozenSet[str] + expmode: str, permissions: frozenset[str] ) -> bool: if expmode == flow_session_expiration_mode.roll_over: if (flow_permission.set_roll_over_expiration_mode diff --git a/course/content.py b/course/content.py index 9477c5cba..ec06de91a 100644 --- a/course/content.py +++ b/course/content.py @@ -28,7 +28,7 @@ import os import re import sys -from typing import Union, cast +from typing import cast import dulwich.objects import dulwich.repo @@ -51,10 +51,10 @@ # {{{ mypy +from collections.abc import Callable from typing import ( TYPE_CHECKING, Any, - Callable, ) @@ -67,8 +67,8 @@ from course.validation import FileSystemFakeRepoTree, ValidationContext from relate.utils import Repo_ish -Date_ish = Union[datetime.datetime, datetime.date] -Datespec = Union[datetime.datetime, datetime.date, str] +Date_ish = datetime.datetime | datetime.date +Datespec = datetime.datetime | datetime.date | str class ChunkRulesDesc(Struct): @@ -662,7 +662,7 @@ def look_up_git_object(repo: dulwich.repo.Repo, from stat import S_ISLNK while name_parts: - if not isinstance(cur_lookup, (Tree, FileSystemFakeRepoTree)): + if not isinstance(cur_lookup, Tree | FileSystemFakeRepoTree): raise ObjectDoesNotExist( _("'%s' is not a directory, cannot lookup nested names") % os.sep.join(processed_name_parts)) @@ -719,7 +719,7 @@ def get_repo_tree(repo: Repo_ish, full_name: str, commit_sha: bytes) -> Tree_ish msg_full_name = full_name if full_name else _("(repo root)") - if isinstance(git_obj, (Tree, FileSystemFakeRepoTree)): + if isinstance(git_obj, Tree | FileSystemFakeRepoTree): return git_obj else: raise ObjectDoesNotExist(_("resource '%s' is not a tree") % msg_full_name) @@ -749,7 +749,7 @@ def get_repo_blob(repo: Repo_ish, full_name: str, commit_sha: bytes) -> Blob_ish msg_full_name = full_name if full_name else _("(repo root)") - if isinstance(git_obj, (Blob, FileSystemFakeRepoFile)): + if isinstance(git_obj, Blob | FileSystemFakeRepoFile): return git_obj else: raise ObjectDoesNotExist(_("resource '%s' is not a file") % msg_full_name) diff --git a/course/flow.py b/course/flow.py index c1eb41828..fb78a3857 100644 --- a/course/flow.py +++ b/course/flow.py @@ -23,7 +23,8 @@ THE SOFTWARE. """ -from typing import TYPE_CHECKING, Any, Iterable, List, Optional +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit @@ -1304,7 +1305,7 @@ def regrade_session( if session.in_progress: with transaction.atomic(): - answer_visits: List[Optional[FlowPageVisit]] = assemble_answer_visits(session) # noqa + answer_visits: list[FlowPageVisit | None] = assemble_answer_visits(session) for i in range(len(answer_visits)): answer_visit = answer_visits[i] diff --git a/course/grades.py b/course/grades.py index 0027296fb..d5c1342e6 100644 --- a/course/grades.py +++ b/course/grades.py @@ -28,9 +28,6 @@ from typing import ( TYPE_CHECKING, Any, - List, - Optional, - Tuple, cast, ) @@ -301,7 +298,7 @@ def grade_key(entry): return (participation.user.last_name.lower(), participation.user.first_name.lower()) - grade_table = sorted(zip(participations, grade_table), key=grade_key) + grade_table = sorted(zip(participations, grade_table, strict=True), key=grade_key) return render_course_page(pctx, "course/gradebook.html", { "grade_table": grade_table, @@ -330,7 +327,7 @@ def export_gradebook_csv(pctx): writer.writerow(fieldnames) - for participation, grades in zip(participations, grade_table): + for participation, grades in zip(participations, grade_table, strict=True): writer.writerow([ participation.user.username, participation.user.last_name, @@ -651,11 +648,14 @@ def view_grades_by_opportunity( page_numbers = list(range(1, 1 + max_page_count)) from course.flow import assemble_page_grades - page_grades: List[List[Optional[FlowPageVisitGrade]]] = assemble_page_grades(all_flow_sessions) # noqa + page_grades: list[list[FlowPageVisitGrade | None]] \ + = assemble_page_grades(all_flow_sessions) - for (_dummy2, grade_info), grade_list in zip(grade_table, page_grades): # type: ignore + for (_dummy2, grade_info), grade_list in \ + zip(grade_table, page_grades, strict=True): # type: ignore # Not all pages exist in all sessions - grades: List[Tuple[Optional[int], Optional[FlowPageVisitGrade]]] = list(enumerate(grade_list)) # noqa + grades: list[tuple[int | None, FlowPageVisitGrade | None]] \ + = list(enumerate(grade_list)) if len(grades) < max_page_count: grades.extend([(None, None)] * (max_page_count - len(grades))) grade_info.grades = grades @@ -999,7 +999,8 @@ def view_single_grade(pctx: CoursePageContext, participation_id: str, flow_desc = get_flow_desc(pctx.repo, pctx.course, opportunity.flow_id, pctx.course_commit_sha) except ObjectDoesNotExist: - flow_sessions_and_session_properties: Optional[List[Tuple[Any, SessionProperties]]] = None # noqa + flow_sessions_and_session_properties: \ + list[tuple[Any, SessionProperties]] | None = None else: flow_sessions_and_session_properties = [] for session in flow_sessions: diff --git a/course/grading.py b/course/grading.py index fa32a2ac2..af2bd8179 100644 --- a/course/grading.py +++ b/course/grading.py @@ -517,7 +517,8 @@ def commit_grade_info(grade): "flow_id": flow_id, "pages": pages, "graders": graders, - "pages_stats_counts": list(zip(pages, stats_table, page_counts)), + "pages_stats_counts": + list(zip(pages, stats_table, page_counts, strict=True)), "grader_counts": grader_counts, }) diff --git a/course/management/commands/bulktofiles.py b/course/management/commands/bulktofiles.py index ff8a7b7f9..b41811e8b 100644 --- a/course/management/commands/bulktofiles.py +++ b/course/management/commands/bulktofiles.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.core.exceptions import ObjectDoesNotExist from django.core.management.base import BaseCommand, CommandError # noqa from django.db import transaction diff --git a/course/management/commands/test.py b/course/management/commands/test.py index 3b02d301f..f12d1ddbf 100644 --- a/course/management/commands/test.py +++ b/course/management/commands/test.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.core.management.commands.test import Command as DjangoTestCommand diff --git a/course/mdx_mathjax.py b/course/mdx_mathjax.py index 90a222399..c0d486ee5 100644 --- a/course/mdx_mathjax.py +++ b/course/mdx_mathjax.py @@ -1,4 +1,5 @@ # Downloaded from https://github.com/mayoff/python-markdown-mathjax/issues/3 +from __future__ import annotations import markdown from markdown.postprocessors import Postprocessor diff --git a/course/models.py b/course/models.py index 506926a87..b9fdaec3f 100644 --- a/course/models.py +++ b/course/models.py @@ -23,10 +23,10 @@ THE SOFTWARE. """ +from collections.abc import Iterable from typing import ( TYPE_CHECKING, Any, - Iterable, cast, ) @@ -1354,7 +1354,7 @@ def validate_stipulations(stip): # pragma: no cover (deprecated and not tested) % ", ".join(set(stip.keys()) - allowed_keys)) if "credit_percent" in stip and not isinstance( - stip["credit_percent"], (int, float)): + stip["credit_percent"], int | float): raise ValidationError(_("credit_percent must be a float")) if ("allowed_session_count" in stip and ( diff --git a/course/page/__init__.py b/course/page/__init__.py index d4f7d7e4e..a7c8086da 100644 --- a/course/page/__init__.py +++ b/course/page/__init__.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/course/page/base.py b/course/page/base.py index 46d03f243..ad52f22d1 100644 --- a/course/page/base.py +++ b/course/page/base.py @@ -23,7 +23,8 @@ THE SOFTWARE. """ -from typing import TYPE_CHECKING, Any, Callable +from collections.abc import Callable +from typing import TYPE_CHECKING, Any import django.forms as forms import django.http diff --git a/course/page/choice.py b/course/page/choice.py index f3e35feb5..82d7716c8 100644 --- a/course/page/choice.py +++ b/course/page/choice.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/course/page/code.py b/course/page/code.py index fc2d85c59..d563b7314 100644 --- a/course/page/code.py +++ b/course/page/code.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -186,7 +189,6 @@ def request_run(run_req, run_timeout, image=None): import errno import http.client as http_client import json - import socket import docker from docker.errors import APIError as DockerAPIError @@ -346,7 +348,7 @@ def check_timeout(): return result - except socket.timeout: + except TimeoutError: return { "result": "timeout", "exec_host": connect_host_ip, diff --git a/course/page/code_feedback.py b/course/page/code_feedback.py index 16854d7b8..29abcaeb3 100644 --- a/course/page/code_feedback.py +++ b/course/page/code_feedback.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -132,7 +135,7 @@ def check_scalar(self, name, ref, data, accuracy_critical=True, rtol=1e-5, atol=1e-8, report_success=True, report_failure=True): import numpy as np - if not isinstance(data, (complex, float, int, np.number)): + if not isinstance(data, complex | float | int | np.number): try: # Check whether data is a sympy number because sympy # numbers do not follow the typical interface diff --git a/course/page/code_run_backend.py b/course/page/code_run_backend.py index c8313f49e..1ae0e2e3b 100644 --- a/course/page/code_run_backend.py +++ b/course/page/code_run_backend.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -22,7 +25,7 @@ import sys import traceback -from typing import Any, Dict +from typing import Any try: @@ -153,7 +156,7 @@ def substitute_correct_code_into_test_code(test_code: str, correct_code: str) -> return "\n".join(new_test_code_lines) -def package_exception(result: Dict[str, Any], what: str) -> None: +def package_exception(result: dict[str, Any], what: str) -> None: tp, val, tb = sys.exc_info() assert tp is not None result["result"] = what diff --git a/course/page/inline.py b/course/page/inline.py index 6be39750b..bef824db2 100644 --- a/course/page/inline.py +++ b/course/page/inline.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2015 Andreas Kloeckner, Dong Zhuang" __license__ = """ @@ -21,7 +24,7 @@ """ import re -from typing import Optional, cast +from typing import cast import django.forms as forms from crispy_forms.bootstrap import PrependedAppendedText @@ -214,7 +217,7 @@ class ShortAnswer(AnswerBase): form_field_class = forms.CharField @staticmethod - def get_length_attr_em(location: str, width_attr: str) -> Optional[float]: + def get_length_attr_em(location: str, width_attr: str) -> float | None: """ generate the length for input box, the unit is 'em' """ @@ -222,7 +225,7 @@ def get_length_attr_em(location: str, width_attr: str) -> Optional[float]: if width_attr is None: return None - if isinstance(width_attr, (int, float)): + if isinstance(width_attr, int | float): return width_attr width_re_match = WIDTH_STR_RE.match(width_attr) diff --git a/course/page/static.py b/course/page/static.py index 2c79eee2c..f7763dde3 100644 --- a/course/page/static.py +++ b/course/page/static.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/course/page/text.py b/course/page/text.py index 85daed287..afcf1e542 100644 --- a/course/page/text.py +++ b/course/page/text.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -23,7 +26,7 @@ import re import sys -from typing import Any, Tuple +from typing import Any import django.forms as forms from django.utils.translation import gettext, gettext_lazy as _ @@ -213,7 +216,7 @@ class TextAnswerMatcher: Only used for answer normalization. Matchers are responsible for case sensitivity themselves. """ - ALLOWED_ATTRIBUTES: Tuple[Any, ...] = () + ALLOWED_ATTRIBUTES: tuple[Any, ...] = () def __init__(self, vctx, location, matcher_desc): self.matcher_desc = matcher_desc @@ -465,7 +468,7 @@ def correct_answer_text(self): def float_or_sympy_evalf(s): - if isinstance(s, (int, float,)): + if isinstance(s, int | float): return s if not isinstance(s, str): diff --git a/course/page/upload.py b/course/page/upload.py index 816e7d91f..fc8d005bf 100644 --- a/course/page/upload.py +++ b/course/page/upload.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/course/receivers.py b/course/receivers.py index 14428fb7a..c5d867b32 100644 --- a/course/receivers.py +++ b/course/receivers.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2016 Dong Zhuang, Andreas Kloeckner" __license__ = """ @@ -20,7 +23,7 @@ THE SOFTWARE. """ -from typing import Any, List, Optional, Tuple, Union +from typing import Any from django.db import transaction from django.db.models.signals import post_save @@ -43,7 +46,7 @@ def update_requested_participation_status( sender: Any, created: bool, - instance: Union[Course, User], + instance: Course | User, **kwargs: Any) -> None: if created: return @@ -80,7 +83,7 @@ def update_requested_participation_status( def may_preapprove_role( - course: Course, user: User) -> Tuple[bool, Optional[List[str]]]: + course: Course, user: User) -> tuple[bool, list[str] | None]: if not user.is_active: return False, None diff --git a/course/sandbox.py b/course/sandbox.py index b1b678f23..fa94e5f2b 100644 --- a/course/sandbox.py +++ b/course/sandbox.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ @@ -20,7 +23,7 @@ THE SOFTWARE. """ -from typing import Any, Optional, Tuple, cast +from typing import Any, cast import django.forms as forms from crispy_forms.layout import Submit @@ -154,10 +157,10 @@ def get_sandbox_data_for_page( stored_data_tuple = pctx.request.session.get(key) # Session storage uses JSON and may turn tuples into lists. - if (isinstance(stored_data_tuple, (list, tuple)) + if (isinstance(stored_data_tuple, list | tuple) and len(stored_data_tuple) == 3): stored_data_page_type, stored_data_page_id, \ - stored_data = cast(Tuple, stored_data_tuple) + stored_data = cast(tuple, stored_data_tuple) if ( stored_data_page_type == page_desc.type @@ -222,7 +225,7 @@ def view_page_sandbox(pctx: CoursePageContext) -> http.HttpResponse: and "clear_response" in request.POST) is_preview_post = (request.method == "POST" and "preview" in request.POST) - def make_form(data: Optional[str] = None) -> PageSandboxForm: + def make_form(data: str | None = None) -> PageSandboxForm: return PageSandboxForm( page_source, "yaml", request.user.editor_mode, gettext("Enter YAML markup for a flow page."), diff --git a/course/tasks.py b/course/tasks.py index 9beccf037..d7c38a938 100644 --- a/course/tasks.py +++ b/course/tasks.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2015 Andreas Kloeckner" __license__ = """ diff --git a/course/templatetags/coursetags.py b/course/templatetags/coursetags.py index 015135ce8..e758ff8b1 100644 --- a/course/templatetags/coursetags.py +++ b/course/templatetags/coursetags.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2016 Dong Zhuang, Andreas Kloeckner" __license__ = """ diff --git a/course/utils.py b/course/utils.py index a364665f0..4a1716757 100644 --- a/course/utils.py +++ b/course/utils.py @@ -24,12 +24,12 @@ """ import datetime +from collections.abc import Iterable from contextlib import ContextDecorator from dataclasses import dataclass from typing import ( TYPE_CHECKING, Any, - Iterable, cast, ) @@ -905,7 +905,7 @@ def repr_js(obj: Any) -> str: return "{%s}" % ", ".join(f"{k}: {repr_js(v)}" for k, v in obj.items()) elif isinstance(obj, bool): return repr(obj).lower() - elif isinstance(obj, (int, float)): + elif isinstance(obj, int | float): return repr(obj) elif isinstance(obj, str): return repr(obj) diff --git a/course/validation.py b/course/validation.py index d3562f4cf..e679f6834 100644 --- a/course/validation.py +++ b/course/validation.py @@ -26,7 +26,7 @@ import datetime import re import sys -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any import dulwich.objects from django.core.exceptions import ObjectDoesNotExist @@ -1643,8 +1643,8 @@ def data(self): return inf.read() -Blob_ish = Union[dulwich.objects.Blob, FileSystemFakeRepoFile] -Tree_ish = Union[dulwich.objects.Tree, FileSystemFakeRepoTree] +Blob_ish = dulwich.objects.Blob | FileSystemFakeRepoFile +Tree_ish = dulwich.objects.Tree | FileSystemFakeRepoTree def validate_course_on_filesystem( diff --git a/course/views.py b/course/views.py index 185dc5758..67ecfa0fe 100644 --- a/course/views.py +++ b/course/views.py @@ -27,7 +27,6 @@ from typing import ( TYPE_CHECKING, Any, - List, cast, ) @@ -1149,7 +1148,7 @@ def grant_exception_stage_3( tags: list[str] = [] if hasattr(flow_desc, "rules"): - tags = cast(List[str], getattr(flow_desc.rules, "tags", [])) + tags = cast(list[str], getattr(flow_desc.rules, "tags", [])) exceptions_created = [] diff --git a/doc/conf.py b/doc/conf.py index 50cde08ce..61be12fea 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys from urllib.request import urlopen diff --git a/exercise-docker.py b/exercise-docker.py index b4536a3e3..1d4c7d462 100644 --- a/exercise-docker.py +++ b/exercise-docker.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + def main(): from course.page.code import request_run diff --git a/manage.py b/manage.py index 034ee0575..a402c1255 100755 --- a/manage.py +++ b/manage.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import annotations + import os import sys diff --git a/pyproject.toml b/pyproject.toml index d597b72a4..3c20f57dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,12 +162,6 @@ yamllint = "^1.32.0" postgres = ["psycopg2"] memcache = ["pylibmc"] -[tool.isort] -profile = "django" -line_length = 85 -lines_after_imports = 2 -src_paths = ["relate", "course", "accounts", "test"] - [tool.ruff] preview = true target-version = "py310" @@ -203,12 +197,6 @@ extend-ignore = [ "RUF012", # mutable class atttributes "UP031", # format "UP032", # f-string - "UP035", - "UP006", - "UP038", - "B905", - "UP007", - "UP041", ] allowed-confusables = ["‐", "–"] @@ -220,13 +208,17 @@ multiline-quotes = "double" [tool.ruff.lint.per-file-ignores] "course/mdx_mathjax.py" = ["N802"] +# config file, no type annotations, avoid scaring users +"local_settings_example.py" = ["I002"] + # copy-pasted from elsewhere, not our problem -"saml-config/attribute-maps/*.py" = ["Q", "E231", "W292", "W291", "E501"] +"saml-config/attribute-maps/*.py" = ["Q", "E231", "W292", "W291", "E501", "I002"] # mostly generated -"course/migrations/*.py" = ["Q", "E501", "RUF012", "F401"] +"course/migrations/*.py" = ["Q", "E501", "RUF012", "F401", "I002", "UP"] "accounts/migrations/*.py" = [ - "Q", "E501", "RUF012", "F401", "E303", "B023", "E731", "N806", "F841"] + "Q", "E501", "RUF012", "F401", "E303", "B023", "E731", "N806", "F841", + "I002"] # TODO "tests/**/*.py" = ["F841", "RUF012"] @@ -234,6 +226,7 @@ multiline-quotes = "double" [tool.ruff.lint.isort] combine-as-imports = true lines-after-imports = 2 +required-imports = ["from __future__ import annotations"] [tool.mypy] strict_optional = true diff --git a/relate/__init__.py b/relate/__init__.py index d9293dd21..0ac756bf9 100644 --- a/relate/__init__.py +++ b/relate/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os diff --git a/relate/__main__.py b/relate/__main__.py index cb6a1261c..5c0b27334 100644 --- a/relate/__main__.py +++ b/relate/__main__.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + if __name__ == "__main__": from relate.bin.relate import main main() diff --git a/relate/bin/relate.py b/relate/bin/relate.py index bf62929da..3bb1efac7 100644 --- a/relate/bin/relate.py +++ b/relate/bin/relate.py @@ -1,8 +1,9 @@ #! /usr/bin/env python3 +from __future__ import annotations import io import sys -from typing import Any, Dict +from typing import Any # {{{ validate_course @@ -175,7 +176,7 @@ def test_code_question(page_desc, repo_root) -> bool: "data_files": data_files, } - response: Dict[str, Any] = {} + response: dict[str, Any] = {} prev_stdin = sys.stdin prev_stdout = sys.stdout diff --git a/relate/celery.py b/relate/celery.py index 75a811732..a561104cc 100644 --- a/relate/celery.py +++ b/relate/celery.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from celery import Celery diff --git a/relate/checks.py b/relate/checks.py index f5e115504..ac4d4e9af 100644 --- a/relate/checks.py +++ b/relate/checks.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ @@ -191,7 +194,7 @@ def check_relate_settings(app_configs, **kwargs): else: ip_ranges = conf.get("ip_ranges", []) if ip_ranges: - if not isinstance(ip_ranges, (list, tuple)): + if not isinstance(ip_ranges, list | tuple): errors.append(RelateCriticalCheckMessage( msg=( INSTANCE_ERROR_PATTERN @@ -237,7 +240,7 @@ def check_relate_settings(app_configs, **kwargs): relate_maintenance_mode_exceptions = getattr( settings, RELATE_MAINTENANCE_MODE_EXCEPTIONS, None) if relate_maintenance_mode_exceptions is not None: - if not isinstance(relate_maintenance_mode_exceptions, (list, tuple)): + if not isinstance(relate_maintenance_mode_exceptions, list | tuple): errors.append(RelateCriticalCheckMessage( msg=(INSTANCE_ERROR_PATTERN % {"location": RELATE_MAINTENANCE_MODE_EXCEPTIONS, @@ -266,7 +269,7 @@ def check_relate_settings(app_configs, **kwargs): relate_session_restart_cooldown_seconds = getattr( settings, RELATE_SESSION_RESTART_COOLDOWN_SECONDS, None) if relate_session_restart_cooldown_seconds is not None: - if not isinstance(relate_session_restart_cooldown_seconds, (int, float)): + if not isinstance(relate_session_restart_cooldown_seconds, int | float): errors.append(RelateCriticalCheckMessage( msg=(INSTANCE_ERROR_PATTERN % {"location": RELATE_SESSION_RESTART_COOLDOWN_SECONDS, @@ -290,7 +293,7 @@ def check_relate_settings(app_configs, **kwargs): relate_ticket_minutes_valid_after_use = getattr( settings, RELATE_TICKET_MINUTES_VALID_AFTER_USE, None) if relate_ticket_minutes_valid_after_use is not None: - if not isinstance(relate_ticket_minutes_valid_after_use, (int, float)): + if not isinstance(relate_ticket_minutes_valid_after_use, int | float): errors.append(RelateCriticalCheckMessage( msg=(INSTANCE_ERROR_PATTERN % {"location": RELATE_TICKET_MINUTES_VALID_AFTER_USE, @@ -531,7 +534,7 @@ def register_startup_checks_extra(): """ startup_checks_extra = getattr(settings, RELATE_STARTUP_CHECKS_EXTRA, None) if startup_checks_extra is not None: - if not isinstance(startup_checks_extra, (list, tuple)): + if not isinstance(startup_checks_extra, list | tuple): raise ImproperlyConfigured( INSTANCE_ERROR_PATTERN % {"location": RELATE_STARTUP_CHECKS_EXTRA, diff --git a/relate/settings.py b/relate/settings.py index 9aec7f778..4e6c24471 100644 --- a/relate/settings.py +++ b/relate/settings.py @@ -1,13 +1,15 @@ """ Django settings for RELATE. """ +from __future__ import annotations import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import sys +from collections.abc import Callable from os.path import join -from typing import Any, Callable, Dict, Union +from typing import Any from django.conf.global_settings import STATICFILES_FINDERS from django.utils.translation import gettext_noop @@ -267,7 +269,11 @@ # {{{ app defaults -RELATE_FACILITIES: Union[None,Dict[str, Dict[str, Any]], Callable[..., Dict[str, Dict[str, Any]]], ] = {} # noqa +RELATE_FACILITIES: ( + None + | dict[str, dict[str, Any]] + | Callable[..., dict[str, dict[str, Any]]] +) = {} RELATE_TICKET_MINUTES_VALID_AFTER_USE = 0 diff --git a/relate/urls.py b/relate/urls.py index fb776f5d8..811a8597e 100644 --- a/relate/urls.py +++ b/relate/urls.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner" __license__ = """ diff --git a/relate/utils.py b/relate/utils.py index acd8366ca..0a9e1fbf4 100644 --- a/relate/utils.py +++ b/relate/utils.py @@ -25,10 +25,9 @@ import datetime +from collections.abc import Mapping from typing import ( Any, - Mapping, - Union, ) from zoneinfo import ZoneInfo @@ -101,7 +100,7 @@ def __delitem__(self, item: bytes) -> None: del self.repo[item] -Repo_ish = Union[dulwich.repo.Repo, SubdirRepoWrapper] +Repo_ish = dulwich.repo.Repo | SubdirRepoWrapper # }}} diff --git a/relate/wsgi.py b/relate/wsgi.py index 5ad29dcbd..28cae2349 100644 --- a/relate/wsgi.py +++ b/relate/wsgi.py @@ -6,6 +6,7 @@ For more information on this file, see https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/ """ +from __future__ import annotations import os diff --git a/tests/base_test_mixins.py b/tests/base_test_mixins.py index 7dc5f5895..0773492c0 100644 --- a/tests/base_test_mixins.py +++ b/tests/base_test_mixins.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang, Andreas Kloeckner, Zesheng Wang" __license__ = """ @@ -372,7 +375,7 @@ def debug_print_response_context_value(self, resp, context_name): value = self.get_response_context_value_by_name(resp, context_name) print("\n-----------context %s-------------" % context_name) - if isinstance(value, (list, tuple)): + if isinstance(value, list | tuple): from course.validation import ValidationWarning for v in value: if isinstance(v, ValidationWarning): @@ -611,7 +614,7 @@ def assertSessionPretendFacilitiesContains(self, session, expected_facilities): "the session doesn't have " "'relate_pretend_facilities' attribute") - if isinstance(expected_facilities, (list, tuple)): + if isinstance(expected_facilities, list | tuple): self.assertTrue(set(expected_facilities).issubset(set(pretended))) else: self.assertTrue(expected_facilities in pretended) @@ -625,7 +628,7 @@ def assertFormErrorLoose(self, response, errors, form_name="form"): # noqa import itertools if errors is None: errors = [] - if not isinstance(errors, (list, tuple)): + if not isinstance(errors, list | tuple): errors = [errors] try: form_errors = ". ".join(list( diff --git a/tests/conftest.py b/tests/conftest.py index 93475f48e..79b2297eb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest diff --git a/tests/constants.py b/tests/constants.py index f9973c4a8..395191290 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from collections import namedtuple diff --git a/tests/factories.py b/tests/factories.py index a5b7f03c2..cb8069bb1 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/resource.py b/tests/resource.py index 6fa0f5473..199d248aa 100644 --- a/tests/resource.py +++ b/tests/resource.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_accounts/test_admin.py b/tests/test_accounts/test_admin.py index 9d3975cae..4a16a2097 100644 --- a/tests/test_accounts/test_admin.py +++ b/tests/test_accounts/test_admin.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_accounts/test_models.py b/tests/test_accounts/test_models.py index 3e4e1b2d6..9a0818f66 100644 --- a/tests/test_accounts/test_models.py +++ b/tests/test_accounts/test_models.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_admin.py b/tests/test_admin.py index 276a43724..d4ea114a0 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_analytics.py b/tests/test_analytics.py index 55a7cb5c9..89cdd53b0 100644 --- a/tests/test_analytics.py +++ b/tests/test_analytics.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_api.py b/tests/test_api.py index f5d9d1331..abdd945fd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2020 Dong Zhuang" __license__ = """ diff --git a/tests/test_auth.py b/tests/test_auth.py index 8fd9b5085..de64159ac 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ @@ -508,7 +511,7 @@ def assertURLEqual(self, url, expected, parse_qs=False): # noqa """ fields = ParseResult._fields - for attr, x, y in zip(fields, urlparse(url), urlparse(expected)): + for attr, x, y in zip(fields, urlparse(url), urlparse(expected), strict=True): if parse_qs and attr == "query": x, y = QueryDict(x), QueryDict(y) if x and y and x != y: diff --git a/tests/test_calendar.py b/tests/test_calendar.py index 44c34abd9..4fc6273eb 100644 --- a/tests/test_calendar.py +++ b/tests/test_calendar.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_checks.py b/tests/test_checks.py index 5b3c816ca..688aec551 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ @@ -63,7 +66,7 @@ def assertCheckMessages(self, # noqa filter_message_id_prefixes = self.msg_id_prefix if isinstance(filter_message_id_prefixes, str): filter_message_id_prefixes = [filter_message_id_prefixes] - assert isinstance(filter_message_id_prefixes, (list, tuple)) + assert isinstance(filter_message_id_prefixes, list | tuple) if expected_ids is None and expected_msgs is None and length is None: raise RuntimeError("At least one parameter should be specified " @@ -78,17 +81,18 @@ def is_id_in_filter(id, filter): try: result_ids, result_msgs = ( list(zip(*[(r.id, r.msg) for r in result - if is_id_in_filter(r.id, filter_message_id_prefixes)]))) + if is_id_in_filter(r.id, filter_message_id_prefixes)], + strict=True))) if expected_ids is not None: - assert isinstance(expected_ids, (list, tuple)) + assert isinstance(expected_ids, list | tuple) if ignore_order: result_ids = tuple(sorted(result_ids)) expected_ids = sorted(expected_ids) self.assertEqual(result_ids, tuple(expected_ids)) if expected_msgs is not None: - assert isinstance(expected_msgs, (list, tuple)) + assert isinstance(expected_msgs, list | tuple) if ignore_order: result_msgs = tuple(sorted(result_msgs)) expected_msgs = sorted(expected_msgs) diff --git a/tests/test_constants.py b/tests/test_constants.py index 2d6bb5625..db628a741 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_content.py b/tests/test_content.py index 0f9e0ec47..f56ef985d 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_enrollment.py b/tests/test_enrollment.py index 3e429a447..31f847666 100644 --- a/tests/test_enrollment.py +++ b/tests/test_enrollment.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_exam.py b/tests/test_exam.py index 1e6c2a46d..bc298f98b 100644 --- a/tests/test_exam.py +++ b/tests/test_exam.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner, Zesheng Wang, Dong Zhuang" __license__ = """ diff --git a/tests/test_flow/test_flow.py b/tests/test_flow/test_flow.py index 0dbec4c65..9c34e2518 100644 --- a/tests/test_flow/test_flow.py +++ b/tests/test_flow/test_flow.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ @@ -393,7 +396,7 @@ def setUp(self): def get_grades_of_opps(self, opp_identifiers=None, as_dict=False, with_grade_time=False): if opp_identifiers is not None: - assert isinstance(opp_identifiers, (list, tuple)) + assert isinstance(opp_identifiers, list | tuple) resp = self.get_my_grades_view() self.assertEqual(resp.status_code, 200) grade_tables = resp.context["grade_table"] diff --git a/tests/test_flow/test_purge_page_view_data.py b/tests/test_flow/test_purge_page_view_data.py index 1c6dd3147..7c10f1304 100644 --- a/tests/test_flow/test_purge_page_view_data.py +++ b/tests/test_flow/test_purge_page_view_data.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_grades/test_csv.py b/tests/test_grades/test_csv.py index 3f79d700d..64625f092 100644 --- a/tests/test_grades/test_csv.py +++ b/tests/test_grades/test_csv.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang, Zesheng Wang, Andreas Kloeckner" __license__ = """ diff --git a/tests/test_grades/test_generic.py b/tests/test_grades/test_generic.py index 08257d69c..d5222c9b6 100644 --- a/tests/test_grades/test_generic.py +++ b/tests/test_grades/test_generic.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Zesheng Wang, Andreas Kloeckner, Zhuang Dong" __license__ = """ diff --git a/tests/test_grades/test_grades.py b/tests/test_grades/test_grades.py index 451e2609a..ef6221f85 100644 --- a/tests/test_grades/test_grades.py +++ b/tests/test_grades/test_grades.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang, Zesheng Wang, Andreas Kloeckner" __license__ = """ diff --git a/tests/test_grading.py b/tests/test_grading.py index 4bfd54e8a..4448fe833 100644 --- a/tests/test_grading.py +++ b/tests/test_grading.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_lint_yaml/test_lint_yaml.py b/tests/test_lint_yaml/test_lint_yaml.py index 441a75efe..ea249a8bb 100644 --- a/tests/test_lint_yaml/test_lint_yaml.py +++ b/tests/test_lint_yaml/test_lint_yaml.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys diff --git a/tests/test_misc.py b/tests/test_misc.py index 90a74071a..ff53cfca9 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_models.py b/tests/test_models.py index a6e4c18fa..dd218d948 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __doc__ = """ This is testing uncovering part of course.models by other tests """ diff --git a/tests/test_pages/markdowns.py b/tests/test_pages/markdowns.py index bab49f249..79099a1dc 100644 --- a/tests/test_pages/markdowns.py +++ b/tests/test_pages/markdowns.py @@ -8,6 +8,8 @@ # }}} # {{{ code questions +from __future__ import annotations + CODE_MARKDWON = """ type: PythonCodeQuestion diff --git a/tests/test_pages/test_base.py b/tests/test_pages/test_base.py index dc5321f00..891324091 100644 --- a/tests/test_pages/test_base.py +++ b/tests/test_pages/test_base.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_choice.py b/tests/test_pages/test_choice.py index 88a5df921..ee195aa4d 100644 --- a/tests/test_pages/test_choice.py +++ b/tests/test_pages/test_choice.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_code.py b/tests/test_pages/test_code.py index 4159ea88a..16b74623e 100644 --- a/tests/test_pages/test_code.py +++ b/tests/test_pages/test_code.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_generic.py b/tests/test_pages/test_generic.py index 036b2937d..7c21b0115 100644 --- a/tests/test_pages/test_generic.py +++ b/tests/test_pages/test_generic.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2014 Andreas Kloeckner, Zesheng Wang, Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_inline.py b/tests/test_pages/test_inline.py index 689aad238..5af3e5f50 100644 --- a/tests/test_pages/test_inline.py +++ b/tests/test_pages/test_inline.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_text.py b/tests/test_pages/test_text.py index 401186f65..5b41fe839 100644 --- a/tests/test_pages/test_text.py +++ b/tests/test_pages/test_text.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/test_upload.py b/tests/test_pages/test_upload.py index 5bd3fc418..585b814bd 100644 --- a/tests/test_pages/test_upload.py +++ b/tests/test_pages/test_upload.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_pages/utils.py b/tests/test_pages/utils.py index f8f098bc9..8e04c8dd0 100644 --- a/tests/test_pages/utils.py +++ b/tests/test_pages/utils.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_postgres.py b/tests/test_postgres.py index 276377743..b7cf7ad8e 100644 --- a/tests/test_postgres.py +++ b/tests/test_postgres.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_receivers.py b/tests/test_receivers.py index 01b96ce5a..a7f27d7e8 100644 --- a/tests/test_receivers.py +++ b/tests/test_receivers.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index 722aacd74..8af780fe7 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Zesheng Wang" __license__ = """ diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 69bb80e2c..5ab3772df 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_utils.py b/tests/test_utils.py index 81e3bd25a..c5a8c4822 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_validation/test_validate_course_content.py b/tests/test_validation/test_validate_course_content.py index 76500d14e..26ab8b8be 100644 --- a/tests/test_validation/test_validate_course_content.py +++ b/tests/test_validation/test_validate_course_content.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_validation/test_validation_tools.py b/tests/test_validation/test_validation_tools.py index 008836ce9..6561b21ef 100644 --- a/tests/test_validation/test_validation_tools.py +++ b/tests/test_validation/test_validation_tools.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2018 Dong Zhuang" __license__ = """ diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 620d3e80d..5ad907ce1 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/test_views.py b/tests/test_views.py index 679f4c15b..052500a85 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + __copyright__ = "Copyright (C) 2017 Dong Zhuang" __license__ = """ diff --git a/tests/utils.py b/tests/utils.py index 0fddefc02..9b5f5ca95 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from functools import wraps from importlib import import_module, reload diff --git a/update-attempt-ids.py b/update-attempt-ids.py index faec65b8a..4b3036f5e 100644 --- a/update-attempt-ids.py +++ b/update-attempt-ids.py @@ -1,3 +1,4 @@ +from __future__ import annotations import django