diff --git a/course/admin.py b/course/admin.py index a025637283e76a3971ddc64505d3ff0d9dab9764..ace25a802a41fd5c869d0bf336fb645281b57e46 100644 --- a/course/admin.py +++ b/course/admin.py @@ -25,7 +25,7 @@ THE SOFTWARE. import six from django.utils.translation import ( - ugettext_lazy as _, string_concat, pgettext) + ugettext_lazy as _, pgettext) from django.contrib import admin from course.models import ( @@ -41,6 +41,7 @@ from course.models import ( GradingOpportunity, GradeChange, InstantMessage, Exam, ExamTicket) from django import forms +from relate.utils import string_concat from course.enrollment import (approve_enrollment, deny_enrollment) from course.constants import ( participation_permission as pperm, diff --git a/course/analytics.py b/course/analytics.py index db42be5ca2678595433b7e2014e76896db800d26..508e5116b7a51a09dd1700598696e375db4a114e 100644 --- a/course/analytics.py +++ b/course/analytics.py @@ -26,7 +26,7 @@ THE SOFTWARE. import six -from django.utils.translation import ugettext as _, pgettext, string_concat +from django.utils.translation import ugettext as _, pgettext from django.shortcuts import ( # noqa render, get_object_or_404, redirect) from django.contrib.auth.decorators import login_required @@ -408,6 +408,7 @@ def make_time_histogram(pctx, flow_id): course=pctx.course, flow_id=flow_id) + from relate.utils import string_concat hist = Histogram( num_log_bins=True, num_bin_title_formatter=( diff --git a/course/auth.py b/course/auth.py index 30da4277f6b426a50396ec853b50ebf08055da48..229bcfeffbf3f2cfbf29d2adfc01637754b7e463 100644 --- a/course/auth.py +++ b/course/auth.py @@ -25,7 +25,7 @@ THE SOFTWARE. """ from typing import cast -from django.utils.translation import ugettext_lazy as _, string_concat +from django.utils.translation import ugettext_lazy as _ from django.shortcuts import ( # noqa render, get_object_or_404, redirect, resolve_url) from django.contrib import messages @@ -61,7 +61,7 @@ from course.constants import ( from course.models import Participation, Course # noqa from accounts.models import User -from relate.utils import StyledForm, StyledModelForm +from relate.utils import StyledForm, StyledModelForm, string_concat from django_select2.forms import ModelSelect2Widget if False: diff --git a/course/calendar.py b/course/calendar.py index 3fbfe8c3b8b1d0059df34b5a9df9905a106b0d6b..7a06232f7526378dd59c98e4ff9c7321488b86b5 100644 --- a/course/calendar.py +++ b/course/calendar.py @@ -28,7 +28,7 @@ import six from six.moves import range from django.utils.translation import ( - ugettext_lazy as _, pgettext_lazy, string_concat) + ugettext_lazy as _, pgettext_lazy) from django.contrib.auth.decorators import login_required from course.utils import course_view, render_course_page from django.core.exceptions import PermissionDenied, ObjectDoesNotExist @@ -41,7 +41,7 @@ from crispy_forms.layout import Submit import datetime from bootstrap3_datetime.widgets import DateTimePicker -from relate.utils import StyledForm, as_local_time +from relate.utils import StyledForm, as_local_time, string_concat from course.constants import ( participation_permission as pperm, ) diff --git a/course/enrollment.py b/course/enrollment.py index 12adfd97f51a224495022ff7dea2b0ce8d0a2691..6e2c914666207d1be85a8e3109d38cb8fe0ad12c 100644 --- a/course/enrollment.py +++ b/course/enrollment.py @@ -28,8 +28,7 @@ from six.moves import intern from django.utils.translation import ( ugettext_lazy as _, - pgettext, - string_concat) + pgettext) from django.shortcuts import ( # noqa render, get_object_or_404, redirect) from django.contrib import messages @@ -65,7 +64,7 @@ from course.auth import UserSearchWidget from course.utils import course_view, render_course_page -from relate.utils import StyledForm, StyledModelForm +from relate.utils import StyledForm, StyledModelForm, string_concat from pytools.lex import RE as REBase # noqa diff --git a/course/exam.py b/course/exam.py index 2470061d4c60dc6b54e40ed03de850539ec85f31..6cd666c7aa36962bc7b22c3f339f2ab3eae197a8 100644 --- a/course/exam.py +++ b/course/exam.py @@ -29,8 +29,7 @@ import six from django.contrib.auth import get_user_model import django.forms as forms from django.utils.translation import ( - ugettext, ugettext_lazy as _, string_concat, - pgettext) + ugettext, ugettext_lazy as _, pgettext) from django.shortcuts import ( # noqa render, get_object_or_404, redirect) from django.core.exceptions import ( # noqa @@ -58,7 +57,7 @@ from course.constants import ( participation_permission as pperm) from course.views import get_now_or_fake_time -from relate.utils import StyledForm +from relate.utils import StyledForm, string_concat # {{{ mypy diff --git a/course/flow.py b/course/flow.py index c7e8bbebc81f5c32c14708ce2b237286862d7bcc..3d7476d68b1d65b6aeee056b622e2209a477d6ea 100644 --- a/course/flow.py +++ b/course/flow.py @@ -26,7 +26,7 @@ THE SOFTWARE. from django.utils import six from django.utils.translation import ( - ugettext, ugettext_lazy as _, string_concat) + ugettext, ugettext_lazy as _) from django.utils.functional import lazy from django.shortcuts import ( # noqa render, get_object_or_404, redirect) @@ -48,7 +48,7 @@ from crispy_forms.helper import FormHelper from relate.utils import ( StyledForm, local_now, as_local_time, - format_datetime_local) + format_datetime_local, string_concat) from crispy_forms.layout import Submit from django_select2.forms import Select2Widget diff --git a/course/grades.py b/course/grades.py index 012a474cb0f65f808f9412dd3659b994c3bd270e..8523337450808607919742b7817164d90da4d5cb 100644 --- a/course/grades.py +++ b/course/grades.py @@ -30,7 +30,7 @@ import six from typing import cast from django.utils.translation import ( - ugettext_lazy as _, pgettext_lazy, ugettext, string_concat) + ugettext_lazy as _, pgettext_lazy, ugettext) from django.shortcuts import ( # noqa render, redirect, get_object_or_404) from django.contrib import messages # noqa @@ -43,7 +43,7 @@ from django.utils.timezone import now from django import http from django.urls import reverse -from relate.utils import StyledForm, StyledModelForm +from relate.utils import StyledForm, StyledModelForm, string_concat from crispy_forms.layout import Submit from bootstrap3_datetime.widgets import DateTimePicker diff --git a/course/models.py b/course/models.py index 6b4c0f32d21fcb0d26d53792fb26ffae2dbc87fc..d2de46769930c290e6663864133e0c1ce3c91965 100644 --- a/course/models.py +++ b/course/models.py @@ -33,13 +33,14 @@ from django.utils.timezone import now from django.urls import reverse from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.utils.translation import ( - ugettext_lazy as _, pgettext_lazy, string_concat) + ugettext_lazy as _, pgettext_lazy) from django.core.validators import RegexValidator from django.db.models.signals import post_save from django.dispatch import receiver from django.conf import settings +from relate.utils import string_concat from course.constants import ( # noqa user_status, USER_STATUS_CHOICES, participation_status, PARTICIPATION_STATUS_CHOICES, diff --git a/course/page/base.py b/course/page/base.py index a8259034145ed22cdea1c17516dd98bd8de25826..4bc5c7eb450d9bac9f36cbb6d009a38beff04471 100644 --- a/course/page/base.py +++ b/course/page/base.py @@ -30,14 +30,13 @@ import django.forms as forms from course.validation import validate_struct, ValidationError from course.constants import MAX_EXTRA_CREDIT_FACTOR -from relate.utils import StyledForm, Struct +from relate.utils import StyledForm, Struct, string_concat from django.forms import ValidationError as FormValidationError from django.utils.safestring import mark_safe from django.utils.functional import lazy from django.utils.translation import ( ugettext_lazy as _, ugettext, - string_concat, ) from django.utils import translation from django.conf import settings diff --git a/course/page/choice.py b/course/page/choice.py index b59669c86293e660410b347d9cf0864b7d05d8ab..edaa2ee9610c1d4a21052e14fbd57d17b103aa8f 100644 --- a/course/page/choice.py +++ b/course/page/choice.py @@ -29,9 +29,9 @@ from six.moves import range import django.forms as forms from django.utils.safestring import mark_safe from django.utils.translation import ( - ugettext_lazy as _, ugettext, string_concat) + ugettext_lazy as _, ugettext) -from relate.utils import StyledForm +from relate.utils import StyledForm, string_concat from course.page.base import ( AnswerFeedback, PageBaseWithTitle, PageBaseWithValue, markup_to_html) from course.content import remove_prefix diff --git a/course/page/code.py b/course/page/code.py index 7933a2364e9a4484d9de3f5308107365fe31df56..520fa22fb687d59c67bb05442f28b1249b7e64b7 100644 --- a/course/page/code.py +++ b/course/page/code.py @@ -30,11 +30,11 @@ from course.validation import ValidationError import django.forms as forms from django.core.exceptions import ObjectDoesNotExist from django.utils.html import escape -from django.utils.translation import ugettext as _, string_concat +from django.utils.translation import ugettext as _ from django.utils import translation from django.conf import settings -from relate.utils import StyledForm +from relate.utils import StyledForm, string_concat from course.page.base import ( PageBaseWithTitle, markup_to_html, PageBaseWithValue, PageBaseWithHumanTextFeedback, diff --git a/course/page/inline.py b/course/page/inline.py index 281b1e5c4c463ac4a8ba2e0aa0b7d2d3b64e25eb..32ecb33445b9e5a777af625ebc009e010c3d8ccd 100644 --- a/course/page/inline.py +++ b/course/page/inline.py @@ -26,13 +26,13 @@ THE SOFTWARE. from django.utils.translation import ( - ugettext_lazy as _, ugettext, string_concat) + ugettext_lazy as _, ugettext) from django.utils.safestring import mark_safe from course.validation import validate_struct, validate_markup, ValidationError from course.content import remove_prefix import django.forms as forms -from relate.utils import Struct, StyledInlineForm +from relate.utils import Struct, StyledInlineForm, string_concat from course.page.base import ( AnswerFeedback, PageBaseWithValue, markup_to_html) diff --git a/course/page/text.py b/course/page/text.py index cb08c1b82cff344b940a996e73034baa5e560849..472f4fed25e96b31a415b4a8b2f0c3d5ae68ac96 100644 --- a/course/page/text.py +++ b/course/page/text.py @@ -27,11 +27,11 @@ THE SOFTWARE. import six from django.utils.translation import ( - ugettext_lazy as _, ugettext, string_concat) + ugettext_lazy as _, ugettext) from course.validation import validate_struct, ValidationError import django.forms as forms -from relate.utils import StyledForm, Struct +from relate.utils import StyledForm, Struct, string_concat from course.page.base import ( AnswerFeedback, PageBaseWithTitle, PageBaseWithValue, markup_to_html, PageBaseWithHumanTextFeedback, PageBaseWithCorrectAnswer, diff --git a/course/page/upload.py b/course/page/upload.py index 22e018d2717304d82524ee004c66921666026955..20c3e12ce49771b05718ca0ee7eab90c20cadf3c 100644 --- a/course/page/upload.py +++ b/course/page/upload.py @@ -26,7 +26,7 @@ THE SOFTWARE. import django.forms as forms -from django.utils.translation import ugettext as _, ugettext_lazy, string_concat +from django.utils.translation import ugettext as _, ugettext_lazy from course.page.base import ( PageBaseWithTitle, PageBaseWithValue, PageBaseWithHumanTextFeedback, @@ -34,7 +34,7 @@ from course.page.base import ( markup_to_html) from course.validation import ValidationError -from relate.utils import StyledForm +from relate.utils import StyledForm, string_concat from crispy_forms.layout import Layout, Field diff --git a/course/utils.py b/course/utils.py index 122af4fde23dbf7905dffe5a3ab1949caaee306d..b8232ee5cfd148d2fdc140079049d07bd056ce75 100644 --- a/course/utils.py +++ b/course/utils.py @@ -34,10 +34,11 @@ from django.shortcuts import ( # noqa from django import http from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ( - ugettext as _, string_concat, pgettext_lazy) + ugettext as _, pgettext_lazy) from codemirror import CodeMirrorTextarea, CodeMirrorJavascript +from relate.utils import string_concat from course.content import ( get_course_repo, get_flow_desc, parse_date_spec, get_course_commit_sha) diff --git a/course/validation.py b/course/validation.py index f3b51b1c8f20edcdb6d8c52b6063bc750887863a..f128cb1fc0d37bc8e6265dd6f50d7503d750eced 100644 --- a/course/validation.py +++ b/course/validation.py @@ -32,14 +32,14 @@ import sys from django.core.exceptions import ObjectDoesNotExist from django.utils.html import escape from django.utils.translation import ( - ugettext_lazy as _, ugettext, string_concat) + ugettext_lazy as _, ugettext) from course.constants import ( FLOW_SESSION_EXPIRATION_MODE_CHOICES, ATTRIBUTES_FILENAME, participation_permission as pperm) from course.content import get_repo_blob -from relate.utils import Struct +from relate.utils import Struct, string_concat # {{{ mypy diff --git a/course/versioning.py b/course/versioning.py index c392c7dae70a3ac8e8a84ba8301c1d1b96532b39..2a78565f7c7105a8f3482eda8543d5920f74793a 100644 --- a/course/versioning.py +++ b/course/versioning.py @@ -37,14 +37,13 @@ from django.utils.translation import ( ugettext, pgettext, pgettext_lazy, - string_concat, ) from django_select2.forms import Select2Widget from bootstrap3_datetime.widgets import DateTimePicker from django.db import transaction -from relate.utils import StyledForm, StyledModelForm +from relate.utils import StyledForm, StyledModelForm, string_concat from crispy_forms.layout import Submit from course.models import ( diff --git a/course/views.py b/course/views.py index 6f1f14cd182bff5af861af60d582f4e66f2734ef..5128d3e2d8da2c05c3cd8f174882576f1e629fcf 100644 --- a/course/views.py +++ b/course/views.py @@ -40,7 +40,6 @@ from django.utils import six from django.utils.translation import ( ugettext_lazy as _, ugettext, - string_concat, pgettext, pgettext_lazy, ) @@ -55,7 +54,7 @@ from django.views.decorators.cache import cache_control from crispy_forms.layout import Submit, Layout, Div -from relate.utils import StyledForm, StyledModelForm +from relate.utils import StyledForm, StyledModelForm, string_concat from bootstrap3_datetime.widgets import DateTimePicker from course.auth import get_pre_impersonation_user diff --git a/relate/utils.py b/relate/utils.py index 1c93bd93a0e0be892f4e67deb3438bd9b15e2d63..1c862b2519e267b2a5ed22bb568cd19aa116e161 100644 --- a/relate/utils.py +++ b/relate/utils.py @@ -36,6 +36,30 @@ from typing import Union if False: from typing import Text, List, Dict, Tuple, Optional, Any # noqa +# {{{ string_concat compatibility for Django >= 1.11 + +try: + from django.utils.text import format_lazy +except ImportError: + def _format_lazy(format_string, *args, **kwargs): + # type(Text, *Any, **Any) -> Text + """ + Apply str.format() on 'format_string' where format_string, args, + and/or kwargs might be lazy. + """ + return format_string.format(*args, **kwargs) + + from django.utils.functional import lazy + format_lazy = lazy(_format_lazy, str) + +try: + from django.utils.translation import string_concat +except ImportError: + def string_concat(*strings): + return format_lazy("{}" * len(strings), *strings) + +# }}} + class StyledForm(forms.Form): def __init__(self, *args, **kwargs): diff --git a/requirements.txt b/requirements.txt index b79796bb87dce3975286f8be2ac54be8e45da7ef..036f50f252b4bfc9cedf5ddeaf1df0f15e84c4ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -django>=1.10,<1.11 +django>=1.10,<1.12 # Automatically renders Django forms in a pretty, Bootstrap-compatible way. django-crispy-forms>=1.5.1