diff --git a/course/sandbox.py b/course/sandbox.py
index 60baa53b1d45cf136872921954368e87a5a98d69..dc64a4bdac82271d55a22eab4b27a8241710a2c5 100644
--- a/course/sandbox.py
+++ b/course/sandbox.py
@@ -29,12 +29,23 @@ from django.utils.safestring import mark_safe
from django.contrib import messages # noqa
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext, ugettext_lazy as _
+from django import http # noqa
from crispy_forms.layout import Submit
from course.utils import course_view, render_course_page
from course.constants import participation_permission as pperm
+from course.utils import ( # noqa
+ CoursePageContext)
+from course.content import FlowPageDesc
+
+
+# {{{ for mypy
+
+from typing import Tuple, Text, Optional, Any, Iterable, Dict, cast # noqa
+
+# }}}
# {{{ sandbox form
@@ -45,6 +56,7 @@ class SandboxForm(forms.Form):
def __init__(self, initial_text,
language_mode, interaction_mode, help_text, *args, **kwargs):
+ # type: (Text, Text, Text, Text, *Any, **Any) -> None
super(SandboxForm, self).__init__(*args, **kwargs)
from crispy_forms.helper import FormHelper
@@ -137,13 +149,14 @@ def view_markup_sandbox(pctx):
# {{{ page sandbox data retriever
def get_sandbox_data_for_page(pctx, page_desc, key):
+ # type: (CoursePageContext, Any, Text) -> Any
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))
and len(stored_data_tuple) == 3):
stored_data_page_type, stored_data_page_id, \
- stored_data = stored_data_tuple
+ stored_data = cast(Tuple, stored_data_tuple)
if (
stored_data_page_type == page_desc.type
@@ -161,6 +174,7 @@ def get_sandbox_data_for_page(pctx, page_desc, key):
class PageSandboxForm(SandboxForm):
def __init__(self, initial_text,
language_mode, interaction_mode, help_text, *args, **kwargs):
+ # type: (Text, Text, Text, Text, *Any, **Any) -> None
super(PageSandboxForm, self).__init__(
initial_text, language_mode, interaction_mode, help_text,
*args, **kwargs)
@@ -177,6 +191,8 @@ class PageSandboxForm(SandboxForm):
@course_view
def view_page_sandbox(pctx):
+ # type: (CoursePageContext) -> http.HttpResponse
+
if not pctx.has_permission(pperm.use_page_sandbox):
raise PermissionDenied()
@@ -203,6 +219,7 @@ def view_page_sandbox(pctx):
is_preview_post = (request.method == "POST" and "preview" in request.POST)
def make_form(data=None):
+ # type: (Optional[Text]) -> PageSandboxForm
return PageSandboxForm(
page_source, "yaml", request.user.editor_mode,
ugettext("Enter YAML markup for a flow page."),
@@ -218,6 +235,9 @@ def view_page_sandbox(pctx):
new_page_source = remove_common_indentation(
edit_form.cleaned_data["content"],
require_leading_newline=False)
+ from course.content import expand_yaml_macros
+ new_page_source = expand_yaml_macros(
+ pctx.repo, pctx.course_commit_sha, new_page_source)
page_desc = dict_to_struct(yaml.load(new_page_source))
if not isinstance(page_desc, Struct):
@@ -242,7 +262,7 @@ def view_page_sandbox(pctx):
ugettext("Page failed to load/validate")
+ ": "
+ "%(err_type)s: %(err_str)s" % {
- "err_type": tp.__name__, "err_str": e})
+ "err_type": tp.__name__, "err_str": e}) # type: ignore
else:
# Yay, it did validate.
@@ -273,7 +293,7 @@ def view_page_sandbox(pctx):
have_valid_page = page_source is not None
if have_valid_page:
- page_desc = dict_to_struct(yaml.load(page_source))
+ page_desc = cast(FlowPageDesc, dict_to_struct(yaml.load(page_source)))
from course.content import instantiate_flow_page
try:
@@ -287,7 +307,7 @@ def view_page_sandbox(pctx):
ugettext("Page failed to load/validate")
+ ": "
+ "%(err_type)s: %(err_str)s" % {
- "err_type": tp.__name__, "err_str": e})
+ "err_type": tp.__name__, "err_str": e}) # type: ignore
have_valid_page = False
if have_valid_page:
@@ -360,7 +380,7 @@ def view_page_sandbox(pctx):
"(change page ID to clear faults)")
+ ": "
+ "%(err_type)s: %(err_str)s" % {
- "err_type": tp.__name__, "err_str": e})
+ "err_type": tp.__name__, "err_str": e}) # type: ignore # noqa: E501
have_valid_page = False
page_form = None
diff --git a/course/utils.py b/course/utils.py
index 54de5de29290368c2e9b764c78171a8d77322199..3cb39dd4c6b6c5475cf48347377123d9555b46b6 100644
--- a/course/utils.py
+++ b/course/utils.py
@@ -36,6 +36,8 @@ from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import (
ugettext as _, string_concat, pgettext_lazy)
+from codemirror import CodeMirrorTextarea, CodeMirrorJavascript
+
from course.content import (
get_course_repo, get_flow_desc,
parse_date_spec, get_course_commit_sha)
@@ -821,15 +823,20 @@ class PageInstanceCache(object):
# {{{ codemirror config
-def get_codemirror_widget(language_mode, interaction_mode,
- config=None, addon_css=(), addon_js=(), dependencies=(),
- read_only=False):
+def get_codemirror_widget(
+ language_mode, # type: Text
+ interaction_mode, # type: Text
+ config=None, # type: Optional[Dict]
+ addon_css=(), # type: Tuple
+ addon_js=(), # type: Tuple
+ dependencies=(), # type: Tuple
+ read_only=False, # type: bool
+ ):
+ # type: (...) -> CodeMirrorTextarea
theme = "default"
if read_only:
theme += " relate-readonly"
- from codemirror import CodeMirrorTextarea, CodeMirrorJavascript
-
from django.urls import reverse
help_text = (_("Press F9 to toggle full-screen mode. ")
+ _("Set editor mode in user profile.")
diff --git a/course/validation.py b/course/validation.py
index 287570e15b0873dedf6b48e9b0da9743ff856e90..06928c335210106888b981a12eab24a2c517bb0a 100644
--- a/course/validation.py
+++ b/course/validation.py
@@ -424,6 +424,7 @@ def validate_staticpage_desc(vctx, location, page_desc):
# {{{ flow validation
def validate_flow_page(vctx, location, page_desc):
+ # type: (ValidationContext, Text, Any) -> None
if not hasattr(page_desc, "id"):
raise ValidationError(
string_concat(
@@ -451,7 +452,7 @@ def validate_flow_page(vctx, location, page_desc):
"%(err_str)s
%(format_exc)s") % { 'location': location, - "err_type": tp.__name__, + "err_type": tp.__name__, # type: ignore "err_str": str(e), 'format_exc': format_exc()})