refactor: Ran pyupgrade on openedx/core
Ran pyupgrade on openedx/core/{lib, tests}
This commit is contained in:
@@ -76,8 +76,8 @@ class BearerAuthentication(BaseAuthentication):
|
||||
token = self.get_access_token(access_token)
|
||||
except AuthenticationFailed as exc:
|
||||
raise AuthenticationFailed({ # lint-amnesty, pylint: disable=raise-missing-from
|
||||
u'error_code': OAUTH2_TOKEN_ERROR,
|
||||
u'developer_message': exc.detail
|
||||
'error_code': OAUTH2_TOKEN_ERROR,
|
||||
'developer_message': exc.detail
|
||||
})
|
||||
|
||||
if not token: # lint-amnesty, pylint: disable=no-else-raise
|
||||
|
||||
@@ -16,7 +16,7 @@ class ExpandableField(Field): # lint-amnesty, pylint: disable=abstract-method
|
||||
assert 'collapsed_serializer' in kwargs and 'expanded_serializer' in kwargs
|
||||
self.collapsed = kwargs.pop('collapsed_serializer')
|
||||
self.expanded = kwargs.pop('expanded_serializer')
|
||||
super(ExpandableField, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def to_representation(self, obj): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""
|
||||
@@ -47,8 +47,8 @@ class AbsoluteURLField(URLField):
|
||||
request = self.context.get('request', None)
|
||||
|
||||
assert request is not None, (
|
||||
u"`%s` requires the request in the serializer context. "
|
||||
u"Add `context={'request': request}` when instantiating the serializer." % self.__class__.__name__
|
||||
"`%s` requires the request in the serializer context. "
|
||||
"Add `context={'request': request}` when instantiating the serializer." % self.__class__.__name__
|
||||
)
|
||||
|
||||
if value.startswith(('http:', 'https:')):
|
||||
|
||||
@@ -22,11 +22,11 @@ class PutAsCreateMixin(CreateModelMixin):
|
||||
# First, try to update the existing instance
|
||||
try:
|
||||
try:
|
||||
return super(PutAsCreateMixin, self).update(request, *args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().update(request, *args, **kwargs)
|
||||
except Http404:
|
||||
# If no instance exists yet, create it.
|
||||
# This is backwards-compatible with the behavior of DRF v2.
|
||||
return super(PutAsCreateMixin, self).create(request, *args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
# Backwards compatibility with DRF v2 behavior, which would catch model-level
|
||||
# validation errors and return a 400
|
||||
|
||||
@@ -71,11 +71,11 @@ class TypedFileUploadParser(FileUploadParser):
|
||||
ext = '.{}'.format(fileparts[1])
|
||||
if ext.lower() not in self.file_extensions[media_type]:
|
||||
errmsg = (
|
||||
u'File extension does not match requested Content-type. '
|
||||
u'Filename: "{filename}", Content-type: "{contenttype}"'
|
||||
'File extension does not match requested Content-type. '
|
||||
'Filename: "{filename}", Content-type: "{contenttype}"'
|
||||
)
|
||||
raise ParseError(errmsg.format(filename=filename, contenttype=media_type))
|
||||
return super(TypedFileUploadParser, self).parse(stream, media_type, parser_context) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().parse(stream, media_type, parser_context)
|
||||
|
||||
|
||||
class MergePatchParser(JSONParser):
|
||||
|
||||
@@ -6,7 +6,6 @@ Serializers to be used in APIs.
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from rest_framework import serializers
|
||||
import six
|
||||
|
||||
|
||||
class CollapsedReferenceSerializer(serializers.HyperlinkedModelSerializer):
|
||||
@@ -32,14 +31,14 @@ class CollapsedReferenceSerializer(serializers.HyperlinkedModelSerializer):
|
||||
|
||||
self.Meta.model = model_class
|
||||
|
||||
super(CollapsedReferenceSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields[id_source] = serializers.CharField(read_only=True)
|
||||
self.fields['url'].view_name = view_name
|
||||
self.fields['url'].lookup_field = lookup_field
|
||||
self.fields['url'].lookup_url_kwarg = lookup_field
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
fields = ("url",)
|
||||
|
||||
|
||||
@@ -48,14 +47,14 @@ class CourseKeyField(serializers.Field):
|
||||
|
||||
def to_representation(self, data): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Convert a course key to unicode. """
|
||||
return six.text_type(data)
|
||||
return str(data)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""Convert unicode to a course key. """
|
||||
try:
|
||||
return CourseKey.from_string(data)
|
||||
except InvalidKeyError as ex:
|
||||
raise serializers.ValidationError(u"Invalid course key: {msg}".format(msg=ex.msg)) # lint-amnesty, pylint: disable=no-member
|
||||
raise serializers.ValidationError(f"Invalid course key: {ex.msg}") # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
|
||||
class UsageKeyField(serializers.Field):
|
||||
@@ -63,11 +62,11 @@ class UsageKeyField(serializers.Field):
|
||||
|
||||
def to_representation(self, data): # lint-amnesty, pylint: disable=arguments-differ
|
||||
"""Convert a usage key to unicode. """
|
||||
return six.text_type(data)
|
||||
return str(data)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""Convert unicode to a usage key. """
|
||||
try:
|
||||
return UsageKey.from_string(data)
|
||||
except InvalidKeyError as ex:
|
||||
raise serializers.ValidationError(u"Invalid usage key: {msg}".format(msg=ex.msg)) # lint-amnesty, pylint: disable=no-member
|
||||
raise serializers.ValidationError(f"Invalid usage key: {ex.msg}") # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
@@ -5,7 +5,6 @@ Helpers for API tests.
|
||||
import base64
|
||||
import json
|
||||
import re
|
||||
import six
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
@@ -50,7 +49,7 @@ class ApiTestCase(TestCase):
|
||||
allow_header = resp.get("Allow")
|
||||
assert allow_header is not None
|
||||
allowed_methods = re.split('[^A-Z]+', allow_header)
|
||||
six.assertCountEqual(self, allowed_methods, expected_methods)
|
||||
self.assertCountEqual(allowed_methods, expected_methods)
|
||||
|
||||
def assertSelfReferential(self, obj):
|
||||
"""Assert that accessing the "url" entry in the given object returns the same object"""
|
||||
@@ -86,6 +85,6 @@ class ApiTestCase(TestCase):
|
||||
# Django rest framework interprets basic auth headers
|
||||
# as an attempt to authenticate with the API.
|
||||
# We don't want this for views available to anonymous users.
|
||||
basic_auth_header = "Basic " + base64.b64encode('username:password'.encode('utf-8')).decode('utf-8')
|
||||
basic_auth_header = "Basic " + base64.b64encode(b'username:password').decode('utf-8')
|
||||
response = getattr(self.client, method)(uri, HTTP_AUTHORIZATION=basic_auth_header)
|
||||
assert response.status_code != 403
|
||||
|
||||
@@ -11,7 +11,7 @@ import jwt
|
||||
JWT_AUTH = 'JWT_AUTH'
|
||||
|
||||
|
||||
class JwtMixin(object):
|
||||
class JwtMixin:
|
||||
""" Mixin with JWT-related helper functions. """
|
||||
|
||||
JWT_SECRET_KEY = getattr(settings, JWT_AUTH)['JWT_SECRET_KEY'] if hasattr(settings, JWT_AUTH) else ''
|
||||
|
||||
@@ -62,7 +62,7 @@ class OAuth2AllowInActiveUsersTests(TestCase): # lint-amnesty, pylint: disable=
|
||||
OAUTH2_BASE_TESTING_URL = '/oauth2-inactive-test/'
|
||||
|
||||
def setUp(self):
|
||||
super(OAuth2AllowInActiveUsersTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.dot_adapter = adapters.DOTAdapter()
|
||||
self.csrf_client = APIClient(enforce_csrf_checks=True)
|
||||
self.username = 'john'
|
||||
@@ -96,7 +96,7 @@ class OAuth2AllowInActiveUsersTests(TestCase): # lint-amnesty, pylint: disable=
|
||||
def _create_authorization_header(self, token=None):
|
||||
if token is None:
|
||||
token = self.dot_access_token.token
|
||||
return "Bearer {0}".format(token)
|
||||
return f"Bearer {token}"
|
||||
|
||||
def get_with_bearer_token(self, target_url, params=None, token=None):
|
||||
"""
|
||||
@@ -224,7 +224,7 @@ class BearerAuthenticationTests(OAuth2AllowInActiveUsersTests): # lint-amnesty,
|
||||
OAUTH2_BASE_TESTING_URL = '/oauth2-test/'
|
||||
|
||||
def setUp(self):
|
||||
super(BearerAuthenticationTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# Since this is testing back to previous version, user should be set to true
|
||||
self.user.is_active = True
|
||||
self.user.save()
|
||||
|
||||
@@ -5,7 +5,6 @@ Test Custom Exceptions
|
||||
import ddt
|
||||
from django.test import TestCase
|
||||
from rest_framework import exceptions as drf_exceptions
|
||||
import six
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -16,8 +15,8 @@ class TestDictExceptionsAllowDictDetails(TestCase):
|
||||
|
||||
def test_drf_errors_are_not_coerced_to_strings(self):
|
||||
# Demonstrate that dictionaries in exceptions are not coerced to strings.
|
||||
exc = drf_exceptions.AuthenticationFailed({u'error_code': -1})
|
||||
assert not isinstance(exc.detail, six.string_types)
|
||||
exc = drf_exceptions.AuthenticationFailed({'error_code': -1})
|
||||
assert not isinstance(exc.detail, str)
|
||||
|
||||
@ddt.data(
|
||||
drf_exceptions.AuthenticationFailed,
|
||||
@@ -27,14 +26,14 @@ class TestDictExceptionsAllowDictDetails(TestCase):
|
||||
drf_exceptions.PermissionDenied,
|
||||
)
|
||||
def test_exceptions_allows_dict_detail(self, exception_class):
|
||||
exc = exception_class({u'error_code': -1})
|
||||
assert exc.detail == {u'error_code': u'-1'}
|
||||
exc = exception_class({'error_code': -1})
|
||||
assert exc.detail == {'error_code': '-1'}
|
||||
|
||||
def test_method_not_allowed_allows_dict_detail(self):
|
||||
exc = drf_exceptions.MethodNotAllowed(u'POST', {u'error_code': -1})
|
||||
assert exc.detail == {u'error_code': u'-1'}
|
||||
exc = drf_exceptions.MethodNotAllowed('POST', {'error_code': -1})
|
||||
assert exc.detail == {'error_code': '-1'}
|
||||
|
||||
def test_not_acceptable_allows_dict_detail(self):
|
||||
exc = drf_exceptions.NotAcceptable({u'error_code': -1}, available_renderers=['application/json'])
|
||||
assert exc.detail == {u'error_code': u'-1'}
|
||||
exc = drf_exceptions.NotAcceptable({'error_code': -1}, available_renderers=['application/json'])
|
||||
assert exc.detail == {'error_code': '-1'}
|
||||
assert exc.available_renderers == ['application/json']
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.test import TestCase
|
||||
from openedx.core.lib.api.fields import AbsoluteURLField
|
||||
|
||||
|
||||
class MockRequest(object):
|
||||
class MockRequest:
|
||||
""" Mock request object. """
|
||||
ROOT = 'http://example.com'
|
||||
|
||||
@@ -20,7 +20,7 @@ class AbsoluteURLFieldTests(TestCase):
|
||||
""" Tests for the AbsoluteURLField. """
|
||||
|
||||
def setUp(self):
|
||||
super(AbsoluteURLFieldTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.field = AbsoluteURLField()
|
||||
self.field._context = {'request': MockRequest()} # pylint:disable=protected-access
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class TestTypedFileUploadParser(APITestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestTypedFileUploadParser, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.parser = parsers.TypedFileUploadParser()
|
||||
self.request_factory = APIRequestFactory()
|
||||
upload_media_types = {'image/png', 'image/jpeg', 'application/octet-stream'}
|
||||
|
||||
@@ -12,7 +12,7 @@ from common.djangoapps.student.tests.factories import UserFactory
|
||||
from openedx.core.lib.api.permissions import IsCourseStaffInstructor, IsMasterCourseStaffInstructor, IsStaffOrOwner
|
||||
|
||||
|
||||
class TestObject(object):
|
||||
class TestObject:
|
||||
""" Fake class for object permission tests. """
|
||||
def __init__(self, user=None, course_id=None):
|
||||
self.user = user
|
||||
@@ -22,7 +22,7 @@ class TestObject(object):
|
||||
class TestCcxObject(TestObject):
|
||||
""" Fake class for object permission for CCX Courses """
|
||||
def __init__(self, user=None, course_id=None):
|
||||
super(TestCcxObject, self).__init__(user, course_id) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(user, course_id)
|
||||
self.coach = user
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class IsCourseStaffInstructorTests(TestCase):
|
||||
""" Test for IsCourseStaffInstructor permission class. """
|
||||
|
||||
def setUp(self):
|
||||
super(IsCourseStaffInstructorTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.permission = IsCourseStaffInstructor()
|
||||
self.coach = UserFactory()
|
||||
self.user = UserFactory()
|
||||
@@ -63,11 +63,11 @@ class IsMasterCourseStaffInstructorTests(TestCase):
|
||||
""" Test for IsMasterCourseStaffInstructorTests permission class. """
|
||||
|
||||
def setUp(self):
|
||||
super(IsMasterCourseStaffInstructorTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.permission = IsMasterCourseStaffInstructor()
|
||||
master_course_id = 'edx/test123/run'
|
||||
self.user = UserFactory()
|
||||
self.get_request = RequestFactory().get('/?master_course_id={}'.format(master_course_id))
|
||||
self.get_request = RequestFactory().get(f'/?master_course_id={master_course_id}')
|
||||
self.get_request.user = self.user
|
||||
self.post_request = RequestFactory().post('/', data={'master_course_id': master_course_id})
|
||||
self.post_request.user = self.user
|
||||
@@ -108,7 +108,7 @@ class IsStaffOrOwnerTests(TestCase):
|
||||
""" Tests for IsStaffOrOwner permission class. """
|
||||
|
||||
def setUp(self):
|
||||
super(IsStaffOrOwnerTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.permission = IsStaffOrOwner()
|
||||
self.request = RequestFactory().get('/')
|
||||
self.obj = TestObject()
|
||||
@@ -148,7 +148,7 @@ class IsStaffOrOwnerTests(TestCase):
|
||||
def test_has_permission_as_owner_with_get(self):
|
||||
""" Owners always have permission to make GET actions. """
|
||||
user = UserFactory()
|
||||
request = RequestFactory().get('/?username={}'.format(user.username))
|
||||
request = RequestFactory().get(f'/?username={user.username}')
|
||||
request.user = user
|
||||
assert self.permission.has_permission(request, None)
|
||||
|
||||
@@ -174,7 +174,7 @@ class IsStaffOrOwnerTests(TestCase):
|
||||
def test_has_permission_as_non_owner(self):
|
||||
""" Non-owners should not have permission. """
|
||||
user = UserFactory()
|
||||
request = RequestFactory().get('/?username={}'.format(user.username))
|
||||
request = RequestFactory().get(f'/?username={user.username}')
|
||||
request.user = UserFactory()
|
||||
assert not self.permission.has_permission(request, None)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class MockAPIView(DeveloperErrorViewMixin, APIView):
|
||||
"""
|
||||
Mock GET handler for testing.
|
||||
"""
|
||||
return Response("Success {}".format(course_id))
|
||||
return Response(f"Success {course_id}")
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^mock/(?P<course_id>.*)/$', MockAPIView.as_view()), # Only works with new-style course keys
|
||||
@@ -51,7 +51,7 @@ class VerifyCourseExistsTestCase(SharedModuleStoreTestCase, APITestCase):
|
||||
org="This", course="IsA", run="Course",
|
||||
default_store=ModuleStoreEnum.Type.split,
|
||||
)
|
||||
response = self.client.get('/mock/{}/'.format(course.id))
|
||||
response = self.client.get(f'/mock/{course.id}/')
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_course_with_outdated_overview_200(self):
|
||||
@@ -62,5 +62,5 @@ class VerifyCourseExistsTestCase(SharedModuleStoreTestCase, APITestCase):
|
||||
course_overview = CourseOverview.get_from_id(course.id)
|
||||
course_overview.version = CourseOverview.VERSION - 1
|
||||
course_overview.save()
|
||||
response = self.client.get('/mock/{}/'.format(course.id))
|
||||
response = self.client.get(f'/mock/{course.id}/')
|
||||
assert response.status_code == 200
|
||||
|
||||
@@ -20,7 +20,6 @@ from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import clone_request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from six import text_type, iteritems
|
||||
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.user_api.accounts import BIO_MAX_LENGTH
|
||||
@@ -35,11 +34,11 @@ class DeveloperErrorResponseException(Exception):
|
||||
Intended to be used with and by DeveloperErrorViewMixin.
|
||||
"""
|
||||
def __init__(self, response):
|
||||
super(DeveloperErrorResponseException, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self.response = response
|
||||
|
||||
|
||||
class DeveloperErrorViewMixin(object):
|
||||
class DeveloperErrorViewMixin:
|
||||
"""
|
||||
A view mixin to handle common error cases other than validation failure
|
||||
(auth failure, method not allowed, etc.) by generating an error response
|
||||
@@ -93,19 +92,19 @@ class DeveloperErrorViewMixin(object):
|
||||
elif isinstance(exc, APIException):
|
||||
return self._make_error_response(exc.status_code, exc.detail)
|
||||
elif isinstance(exc, Http404) or isinstance(exc, ObjectDoesNotExist): # lint-amnesty, pylint: disable=consider-merging-isinstance
|
||||
return self._make_error_response(404, text_type(exc) or "Not found.")
|
||||
return self._make_error_response(404, str(exc) or "Not found.")
|
||||
elif isinstance(exc, ValidationError):
|
||||
return self._make_validation_error_response(exc)
|
||||
else:
|
||||
raise # lint-amnesty, pylint: disable=misplaced-bare-raise
|
||||
|
||||
|
||||
class ExpandableFieldViewMixin(object):
|
||||
class ExpandableFieldViewMixin:
|
||||
"""A view mixin to add expansion information to the serializer context for later use by an ExpandableField."""
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Adds expand information from query parameters to the serializer context to support expandable fields."""
|
||||
result = super(ExpandableFieldViewMixin, self).get_serializer_context() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
result = super().get_serializer_context()
|
||||
result['expand'] = [x for x in self.request.query_params.get('expand', '').split(',') if x]
|
||||
return result
|
||||
|
||||
@@ -141,7 +140,7 @@ def clean_errors(error):
|
||||
This cursively handles the nesting of errors.
|
||||
"""
|
||||
if isinstance(error, ErrorDetail):
|
||||
return text_type(error)
|
||||
return str(error)
|
||||
if isinstance(error, list):
|
||||
return [clean_errors(el) for el in error]
|
||||
else:
|
||||
@@ -153,15 +152,15 @@ def add_serializer_errors(serializer, data, field_errors):
|
||||
"""Adds errors from serializer validation to field_errors. data is the original data to deserialize."""
|
||||
if not serializer.is_valid():
|
||||
errors = serializer.errors
|
||||
for key, error in iteritems(errors):
|
||||
for key, error in errors.items():
|
||||
error = clean_errors(error)
|
||||
if key == 'bio':
|
||||
user_message = _(u"The about me field must be at most {} characters long.".format(BIO_MAX_LENGTH)) # lint-amnesty, pylint: disable=translation-of-non-string
|
||||
user_message = _(f"The about me field must be at most {BIO_MAX_LENGTH} characters long.") # lint-amnesty, pylint: disable=translation-of-non-string
|
||||
else:
|
||||
user_message = _(u"This value is invalid.")
|
||||
user_message = _("This value is invalid.")
|
||||
|
||||
field_errors[key] = {
|
||||
'developer_message': u"Value '{field_value}' is not valid for field '{field_name}': {error}".format(
|
||||
'developer_message': "Value '{field_value}' is not valid for field '{field_name}': {error}".format(
|
||||
field_value=data.get(key, ''), field_name=key, error=error
|
||||
),
|
||||
'user_message': user_message,
|
||||
@@ -323,12 +322,12 @@ class LazySequence(Sequence):
|
||||
|
||||
def __repr__(self):
|
||||
if self._exhausted:
|
||||
return u"LazySequence({!r}, {!r})".format(
|
||||
return "LazySequence({!r}, {!r})".format(
|
||||
self._data,
|
||||
self.est_len,
|
||||
)
|
||||
else:
|
||||
return u"LazySequence(itertools.chain({!r}, {!r}), {!r})".format(
|
||||
return "LazySequence(itertools.chain({!r}, {!r}), {!r})".format(
|
||||
self._data,
|
||||
self.iterable,
|
||||
self.est_len,
|
||||
@@ -386,7 +385,7 @@ def require_post_params(required_params):
|
||||
request = args[0]
|
||||
missing_params = set(required_params) - set(request.POST.keys())
|
||||
if missing_params:
|
||||
msg = u"Missing POST parameters: {missing}".format(
|
||||
msg = "Missing POST parameters: {missing}".format(
|
||||
missing=", ".join(missing_params)
|
||||
)
|
||||
return HttpResponseBadRequest(msg)
|
||||
@@ -426,7 +425,7 @@ def verify_course_exists(view_func):
|
||||
if not CourseOverview.course_exists(course_key):
|
||||
raise self.api_error(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
developer_message=u"Requested grade for unknown course {course}".format(course=text_type(course_key)),
|
||||
developer_message="Requested grade for unknown course {course}".format(course=str(course_key)),
|
||||
error_code='course_does_not_exist'
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import dateutil.parser
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
import requests
|
||||
import six
|
||||
|
||||
from .models import (
|
||||
Bundle,
|
||||
@@ -43,7 +42,7 @@ def api_request(method, url, **kwargs):
|
||||
"""
|
||||
if not settings.BLOCKSTORE_API_AUTH_TOKEN:
|
||||
raise ImproperlyConfigured("Cannot use Blockstore unless BLOCKSTORE_API_AUTH_TOKEN is set.")
|
||||
kwargs.setdefault('headers', {})['Authorization'] = "Token {}".format(settings.BLOCKSTORE_API_AUTH_TOKEN)
|
||||
kwargs.setdefault('headers', {})['Authorization'] = f"Token {settings.BLOCKSTORE_API_AUTH_TOKEN}"
|
||||
response = requests.request(method, url, **kwargs)
|
||||
if response.status_code == 404:
|
||||
raise NotFound
|
||||
@@ -115,7 +114,7 @@ def get_collection(collection_uuid):
|
||||
try:
|
||||
data = api_request('get', api_url('collections', str(collection_uuid)))
|
||||
except NotFound:
|
||||
raise CollectionNotFound("Collection {} does not exist.".format(collection_uuid)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise CollectionNotFound(f"Collection {collection_uuid} does not exist.") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return _collection_from_response(data)
|
||||
|
||||
|
||||
@@ -170,7 +169,7 @@ def get_bundle(bundle_uuid):
|
||||
try:
|
||||
data = api_request('get', api_url('bundles', str(bundle_uuid)))
|
||||
except NotFound:
|
||||
raise BundleNotFound("Bundle {} does not exist.".format(bundle_uuid)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise BundleNotFound(f"Bundle {bundle_uuid} does not exist.") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return _bundle_from_response(data)
|
||||
|
||||
|
||||
@@ -202,7 +201,8 @@ def update_bundle(bundle_uuid, **fields):
|
||||
if "collection_uuid" in fields:
|
||||
data["collection_uuid"] = str(fields.pop("collection_uuid"))
|
||||
if fields:
|
||||
raise ValueError("Unexpected extra fields passed to update_bundle: {}".format(fields.keys()))
|
||||
raise ValueError(f"Unexpected extra fields passed " # pylint: disable=dict-keys-not-iterating
|
||||
f"to update_bundle: {fields.keys()}")
|
||||
result = api_request('patch', api_url('bundles', str(bundle_uuid)), json=data)
|
||||
return _bundle_from_response(result)
|
||||
|
||||
@@ -224,7 +224,7 @@ def get_draft(draft_uuid):
|
||||
try:
|
||||
data = api_request('get', api_url('drafts', str(draft_uuid)))
|
||||
except NotFound:
|
||||
raise DraftNotFound("Draft does not exist: {}".format(draft_uuid)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise DraftNotFound(f"Draft does not exist: {draft_uuid}") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return _draft_from_response(data)
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ def get_bundle_file_metadata(bundle_uuid, path, use_draft=None):
|
||||
return files_dict[path]
|
||||
except KeyError:
|
||||
raise BundleFileNotFound( # lint-amnesty, pylint: disable=raise-missing-from
|
||||
"Bundle {} (draft: {}) does not contain a file {}".format(bundle_uuid, use_draft, path)
|
||||
f"Bundle {bundle_uuid} (draft: {use_draft}) does not contain a file {path}"
|
||||
)
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ def encode_str_for_draft(input_str):
|
||||
"""
|
||||
Given a string, return UTF-8 representation that is then base64 encoded.
|
||||
"""
|
||||
if isinstance(input_str, six.text_type):
|
||||
if isinstance(input_str, str):
|
||||
binary = input_str.encode('utf8')
|
||||
else:
|
||||
binary = input_str
|
||||
|
||||
@@ -6,7 +6,6 @@ from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
import attr
|
||||
import six
|
||||
|
||||
|
||||
def _convert_to_uuid(value):
|
||||
@@ -16,50 +15,50 @@ def _convert_to_uuid(value):
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class Collection(object):
|
||||
class Collection:
|
||||
"""
|
||||
Metadata about a blockstore collection
|
||||
"""
|
||||
uuid = attr.ib(type=UUID, converter=_convert_to_uuid)
|
||||
title = attr.ib(type=six.text_type)
|
||||
title = attr.ib(type=str)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class Bundle(object):
|
||||
class Bundle:
|
||||
"""
|
||||
Metadata about a blockstore bundle
|
||||
"""
|
||||
uuid = attr.ib(type=UUID, converter=_convert_to_uuid)
|
||||
title = attr.ib(type=six.text_type)
|
||||
description = attr.ib(type=six.text_type)
|
||||
slug = attr.ib(type=six.text_type)
|
||||
title = attr.ib(type=str)
|
||||
description = attr.ib(type=str)
|
||||
slug = attr.ib(type=str)
|
||||
drafts = attr.ib(type=dict) # Dict of drafts, where keys are the draft names and values are draft UUIDs
|
||||
# Note that if latest_version is 0, it means that no versions yet exist
|
||||
latest_version = attr.ib(type=int, validator=attr.validators.instance_of(int))
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class Draft(object):
|
||||
class Draft:
|
||||
"""
|
||||
Metadata about a blockstore draft
|
||||
"""
|
||||
uuid = attr.ib(type=UUID, converter=_convert_to_uuid)
|
||||
bundle_uuid = attr.ib(type=UUID, converter=_convert_to_uuid)
|
||||
name = attr.ib(type=six.text_type)
|
||||
name = attr.ib(type=str)
|
||||
updated_at = attr.ib(type=datetime, validator=attr.validators.instance_of(datetime))
|
||||
files = attr.ib(type=dict)
|
||||
links = attr.ib(type=dict)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class BundleFile(object):
|
||||
class BundleFile:
|
||||
"""
|
||||
Metadata about a file in a blockstore bundle or draft.
|
||||
"""
|
||||
path = attr.ib(type=six.text_type)
|
||||
path = attr.ib(type=str)
|
||||
size = attr.ib(type=int)
|
||||
url = attr.ib(type=six.text_type)
|
||||
hash_digest = attr.ib(type=six.text_type)
|
||||
url = attr.ib(type=str)
|
||||
hash_digest = attr.ib(type=str)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
@@ -71,17 +70,17 @@ class DraftFile(BundleFile):
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class LinkReference(object):
|
||||
class LinkReference:
|
||||
"""
|
||||
A pointer to a specific BundleVersion
|
||||
"""
|
||||
bundle_uuid = attr.ib(type=UUID, converter=_convert_to_uuid)
|
||||
version = attr.ib(type=int)
|
||||
snapshot_digest = attr.ib(type=six.text_type)
|
||||
snapshot_digest = attr.ib(type=str)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class LinkDetails(object):
|
||||
class LinkDetails:
|
||||
"""
|
||||
Details about a specific link in a BundleVersion or Draft
|
||||
"""
|
||||
|
||||
@@ -37,7 +37,7 @@ def ensure_queue_env(desired_env):
|
||||
(
|
||||
queue
|
||||
for queue in queues
|
||||
if '.{}.'.format(desired_env) in queue
|
||||
if f'.{desired_env}.' in queue
|
||||
),
|
||||
None
|
||||
)
|
||||
|
||||
@@ -43,13 +43,13 @@ def _get_prerequisite_milestone(prereq_content_key):
|
||||
))
|
||||
|
||||
if not milestones:
|
||||
log.warning(u"Could not find gating milestone for prereq UsageKey %s", prereq_content_key)
|
||||
log.warning("Could not find gating milestone for prereq UsageKey %s", prereq_content_key)
|
||||
return None
|
||||
|
||||
if len(milestones) > 1:
|
||||
# We should only ever have one gating milestone per UsageKey
|
||||
# Log a warning here and pick the first one
|
||||
log.warning(u"Multiple gating milestones found for prereq UsageKey %s", prereq_content_key)
|
||||
log.warning("Multiple gating milestones found for prereq UsageKey %s", prereq_content_key)
|
||||
|
||||
return milestones[0]
|
||||
|
||||
@@ -68,7 +68,7 @@ def _validate_min_score(min_score):
|
||||
GatingValidationError: If the minimum score is not valid
|
||||
"""
|
||||
if min_score:
|
||||
message = _(u"%(min_score)s is not a valid grade percentage") % {'min_score': min_score}
|
||||
message = _("%(min_score)s is not a valid grade percentage") % {'min_score': min_score}
|
||||
try:
|
||||
min_score = int(min_score)
|
||||
except ValueError:
|
||||
@@ -183,7 +183,7 @@ def add_prerequisite(course_key, prereq_content_key):
|
||||
"""
|
||||
milestone = milestones_api.add_milestone(
|
||||
{
|
||||
'name': _(u'Gating milestone for {usage_key}').format(usage_key=str(prereq_content_key)),
|
||||
'name': _('Gating milestone for {usage_key}').format(usage_key=str(prereq_content_key)),
|
||||
'namespace': "{usage_key}{qualifier}".format(
|
||||
usage_key=prereq_content_key,
|
||||
qualifier=GATING_NAMESPACE_QUALIFIER
|
||||
@@ -445,7 +445,7 @@ def get_subsection_grade_percentage(subsection_usage_key, user):
|
||||
subsection_grade = subsection_grade_factory.update(subsection_structure[subsection_usage_key])
|
||||
return _get_subsection_percentage(subsection_grade)
|
||||
except ItemNotFoundError as err:
|
||||
log.warning(u"Could not find course_block for subsection=%s error=%s", subsection_usage_key, err)
|
||||
log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err)
|
||||
return 0.0
|
||||
|
||||
|
||||
@@ -487,7 +487,7 @@ def get_subsection_completion_percentage(subsection_usage_key, user):
|
||||
)
|
||||
|
||||
except ItemNotFoundError as err:
|
||||
log.warning(u"Could not find course_block for subsection=%s error=%s", subsection_usage_key, err)
|
||||
log.warning("Could not find course_block for subsection=%s error=%s", subsection_usage_key, err)
|
||||
|
||||
return subsection_completion_percentage
|
||||
|
||||
@@ -505,14 +505,14 @@ def _get_minimum_required_percentage(milestone):
|
||||
min_score = int(requirements.get('min_score'))
|
||||
except (ValueError, TypeError):
|
||||
log.warning(
|
||||
u'Gating: Failed to find minimum score for gating milestone %s, defaulting to 100',
|
||||
'Gating: Failed to find minimum score for gating milestone %s, defaulting to 100',
|
||||
json.dumps(milestone)
|
||||
)
|
||||
try:
|
||||
min_completion = int(requirements.get('min_completion', 0))
|
||||
except (ValueError, TypeError):
|
||||
log.warning(
|
||||
u'Gating: Failed to find minimum completion percentage for gating milestone %s, defaulting to 100',
|
||||
'Gating: Failed to find minimum completion percentage for gating milestone %s, defaulting to 100',
|
||||
json.dumps(milestone)
|
||||
)
|
||||
return min_score, min_completion
|
||||
|
||||
@@ -5,7 +5,7 @@ A wrapper class to communicate with Gating api
|
||||
from . import api as gating_api
|
||||
|
||||
|
||||
class GatingService(object):
|
||||
class GatingService:
|
||||
"""
|
||||
An XBlock service to talk to the Gating api.
|
||||
"""
|
||||
|
||||
@@ -3,15 +3,14 @@ Tests for the gating API
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
import six
|
||||
from completion.models import BlockCompletion
|
||||
from ddt import data, ddt, unpack
|
||||
from django.conf import settings
|
||||
from milestones import api as milestones_api
|
||||
from milestones.tests.utils import MilestonesTestCaseMixin
|
||||
from mock import Mock, patch
|
||||
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.gating import api as lms_gating_api
|
||||
@@ -37,7 +36,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
"""
|
||||
Initial data setup
|
||||
"""
|
||||
super(TestGatingApi, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
# create course
|
||||
self.course = CourseFactory.create(
|
||||
@@ -77,7 +76,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
|
||||
self.generic_milestone = {
|
||||
'name': 'Test generic milestone',
|
||||
'namespace': six.text_type(self.seq1.location),
|
||||
'namespace': str(self.seq1.location),
|
||||
}
|
||||
|
||||
@patch('openedx.core.lib.gating.api.log.warning')
|
||||
@@ -147,7 +146,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
prereqs = gating_api.get_prerequisites(self.course.id)
|
||||
assert len(prereqs) == 1
|
||||
assert prereqs[0]['block_display_name'] == self.seq1.display_name
|
||||
assert prereqs[0]['block_usage_key'] == six.text_type(self.seq1.location)
|
||||
assert prereqs[0]['block_usage_key'] == str(self.seq1.location)
|
||||
assert gating_api.is_prerequisite(self.course.id, self.seq1.location)
|
||||
|
||||
gating_api.remove_prerequisite(self.seq1.location)
|
||||
@@ -164,7 +163,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
prereq_content_key, min_score, min_completion = gating_api.get_required_content(
|
||||
self.course.id, self.seq2.location
|
||||
)
|
||||
assert prereq_content_key == six.text_type(self.seq1.location)
|
||||
assert prereq_content_key == str(self.seq1.location)
|
||||
assert min_score == 100
|
||||
assert min_completion == 100
|
||||
|
||||
@@ -193,7 +192,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
milestone = milestones_api.get_course_content_milestones(self.course.id, self.seq2.location, 'requires')[0]
|
||||
|
||||
assert gating_api.get_gated_content(self.course, staff) == []
|
||||
assert gating_api.get_gated_content(self.course, student) == [six.text_type(self.seq2.location)]
|
||||
assert gating_api.get_gated_content(self.course, student) == [str(self.seq2.location)]
|
||||
|
||||
milestones_api.add_user_milestone({'id': student.id}, milestone)
|
||||
|
||||
@@ -300,7 +299,7 @@ class TestGatingApi(ModuleStoreTestCase, MilestonesTestCaseMixin):
|
||||
component = ItemFactory.create(
|
||||
parent_location=self.vertical.location,
|
||||
category=component_type,
|
||||
display_name=u'{} block'.format(component_type)
|
||||
display_name=f'{component_type} block'
|
||||
)
|
||||
|
||||
with patch.object(BlockCompletion, 'get_learning_context_completions') as course_block_completions_mock:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import json
|
||||
import pprint
|
||||
import six
|
||||
|
||||
|
||||
def assert_event_matches(expected, actual, tolerate=None):
|
||||
@@ -86,7 +85,7 @@ def assert_event_matches(expected, actual, tolerate=None):
|
||||
raise AssertionError('Unexpected differences found in structs:\n\n' + '\n'.join(message_lines))
|
||||
|
||||
|
||||
class EventMatchTolerates(object):
|
||||
class EventMatchTolerates:
|
||||
"""
|
||||
Represents groups of flags that specify the level of tolerance for deviation between an expected event and an actual
|
||||
event.
|
||||
@@ -196,7 +195,7 @@ def parse_event_payload(event):
|
||||
parsed. It will never modify the event in place.
|
||||
"""
|
||||
payload_key = 'event' if 'event' in event else 'data'
|
||||
if payload_key in event and isinstance(event[payload_key], six.string_types):
|
||||
if payload_key in event and isinstance(event[payload_key], str):
|
||||
event = event.copy()
|
||||
try:
|
||||
event[payload_key] = json.loads(event[payload_key])
|
||||
@@ -224,18 +223,18 @@ def compare_structs(expected, actual, should_strict_compare=None, path=None):
|
||||
actual_keys = frozenset(list(actual.keys()))
|
||||
|
||||
for key in expected_keys - actual_keys:
|
||||
differences.append(u'{0}: not found in actual'.format(_path_to_string(path + [key])))
|
||||
differences.append('{}: not found in actual'.format(_path_to_string(path + [key])))
|
||||
|
||||
if should_strict_compare is not None and should_strict_compare(path):
|
||||
for key in actual_keys - expected_keys:
|
||||
differences.append(u'{0}: only defined in actual'.format(_path_to_string(path + [key])))
|
||||
differences.append('{}: only defined in actual'.format(_path_to_string(path + [key])))
|
||||
|
||||
for key in expected_keys & actual_keys:
|
||||
child_differences = compare_structs(expected[key], actual[key], should_strict_compare, path + [key])
|
||||
differences.extend(child_differences)
|
||||
|
||||
elif expected != actual:
|
||||
differences.append(u'{path}: {a} != {b} (expected != actual)'.format(
|
||||
differences.append('{path}: {a} != {b} (expected != actual)'.format(
|
||||
path=_path_to_string(path),
|
||||
a=repr(expected),
|
||||
b=repr(actual)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for cache_utils.py
|
||||
"""
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from edx_django_utils.cache import RequestCache
|
||||
from mock import Mock
|
||||
|
||||
from openedx.core.lib.cache_utils import request_cached
|
||||
|
||||
@@ -177,16 +175,16 @@ class TestRequestCachedDecorator(TestCase):
|
||||
A dummy function that expects an str and unicode arguments.
|
||||
"""
|
||||
assert isinstance(arg1, str), 'First parameter has to be of type `str`'
|
||||
assert isinstance(arg2, six.text_type), 'Second parameter has to be of type `unicode`'
|
||||
assert isinstance(arg2, str), 'Second parameter has to be of type `unicode`'
|
||||
return True
|
||||
|
||||
assert dummy_function('Hello', u'World'), 'Should be callable with ASCII chars'
|
||||
assert dummy_function('H∂llå', u'Wørld'), 'Should be callable with non-ASCII chars'
|
||||
assert dummy_function('Hello', 'World'), 'Should be callable with ASCII chars'
|
||||
assert dummy_function('H∂llå', 'Wørld'), 'Should be callable with non-ASCII chars'
|
||||
|
||||
wrapped = request_cached()(dummy_function) # lint-amnesty, pylint: disable=no-value-for-parameter
|
||||
|
||||
assert wrapped('Hello', u'World'), 'Wrapper should handle ASCII only chars'
|
||||
assert wrapped('H∂llå', u'Wørld'), 'Wrapper should handle non-ASCII chars'
|
||||
assert wrapped('Hello', 'World'), 'Wrapper should handle ASCII only chars'
|
||||
assert wrapped('H∂llå', 'Wørld'), 'Wrapper should handle non-ASCII chars'
|
||||
|
||||
def test_request_cached_with_none_result(self):
|
||||
"""
|
||||
@@ -276,7 +274,7 @@ class TestRequestCachedDecorator(TestCase):
|
||||
"""Simple wrapper to let us decorate our mock."""
|
||||
return to_be_wrapped(*args, **kwargs)
|
||||
|
||||
arg_map_function = lambda arg: six.text_type(arg == 1)
|
||||
arg_map_function = lambda arg: str(arg == 1)
|
||||
wrapped = request_cached(arg_map_function=arg_map_function)(mock_wrapper) # lint-amnesty, pylint: disable=no-value-for-parameter
|
||||
|
||||
# This will be a miss, and make an underlying call.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
""" Tests of specific tabs. """
|
||||
from unittest import TestCase
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from mock import Mock, patch
|
||||
|
||||
import xmodule.tabs as xmodule_tabs
|
||||
from openedx.core.lib.course_tabs import CourseTabPluginManager
|
||||
@@ -40,7 +40,7 @@ class KeyCheckerTestCase(TestCase):
|
||||
"""Test cases for KeyChecker class"""
|
||||
|
||||
def setUp(self):
|
||||
super(KeyCheckerTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.valid_keys = ['a', 'b']
|
||||
self.invalid_keys = ['a', 'v', 'g']
|
||||
@@ -58,7 +58,7 @@ class NeedNameTestCase(TestCase):
|
||||
"""Test cases for NeedName validator"""
|
||||
|
||||
def setUp(self):
|
||||
super(NeedNameTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.valid_dict1 = {'a': 1, 'name': 2}
|
||||
self.valid_dict2 = {'name': 1}
|
||||
|
||||
@@ -4,7 +4,6 @@ Tests for functionality in openedx/core/lib/courses.py.
|
||||
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
@@ -30,25 +29,25 @@ class CourseImageTestCase(ModuleStoreTestCase):
|
||||
"""Test image URL formatting."""
|
||||
course = CourseFactory.create()
|
||||
self.verify_url(
|
||||
six.text_type(course.id.make_asset_key('asset', course.course_image)),
|
||||
str(course.id.make_asset_key('asset', course.course_image)),
|
||||
course_image_url(course)
|
||||
)
|
||||
|
||||
def test_non_ascii_image_name(self):
|
||||
""" Verify that non-ascii image names are cleaned """
|
||||
course_image = u'before_\N{SNOWMAN}_after.jpg'
|
||||
course_image = 'before_\N{SNOWMAN}_after.jpg'
|
||||
course = CourseFactory.create(course_image=course_image)
|
||||
self.verify_url(
|
||||
six.text_type(course.id.make_asset_key('asset', course_image.replace(u'\N{SNOWMAN}', '_'))),
|
||||
str(course.id.make_asset_key('asset', course_image.replace('\N{SNOWMAN}', '_'))),
|
||||
course_image_url(course)
|
||||
)
|
||||
|
||||
def test_spaces_in_image_name(self):
|
||||
""" Verify that image names with spaces in them are cleaned """
|
||||
course_image = u'before after.jpg'
|
||||
course = CourseFactory.create(course_image=u'before after.jpg')
|
||||
course_image = 'before after.jpg'
|
||||
course = CourseFactory.create(course_image='before after.jpg')
|
||||
self.verify_url(
|
||||
six.text_type(course.id.make_asset_key('asset', course_image.replace(" ", "_"))),
|
||||
str(course.id.make_asset_key('asset', course_image.replace(" ", "_"))),
|
||||
course_image_url(course)
|
||||
)
|
||||
|
||||
@@ -65,18 +64,18 @@ class CourseImageTestCase(ModuleStoreTestCase):
|
||||
|
||||
def test_get_banner_image_url(self):
|
||||
"""Test banner image URL formatting."""
|
||||
banner_image = u'banner_image.jpg'
|
||||
banner_image = 'banner_image.jpg'
|
||||
course = CourseFactory.create(banner_image=banner_image)
|
||||
self.verify_url(
|
||||
six.text_type(course.id.make_asset_key('asset', banner_image)),
|
||||
str(course.id.make_asset_key('asset', banner_image)),
|
||||
course_image_url(course, 'banner_image')
|
||||
)
|
||||
|
||||
def test_get_video_thumbnail_image_url(self):
|
||||
"""Test video thumbnail image URL formatting."""
|
||||
thumbnail_image = u'thumbnail_image.jpg'
|
||||
thumbnail_image = 'thumbnail_image.jpg'
|
||||
course = CourseFactory.create(video_thumbnail_image=thumbnail_image)
|
||||
self.verify_url(
|
||||
six.text_type(course.id.make_asset_key('asset', thumbnail_image)),
|
||||
str(course.id.make_asset_key('asset', thumbnail_image)),
|
||||
course_image_url(course, 'video_thumbnail_image')
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ class TestDerivedSettings(TestCase):
|
||||
Test settings that are derived from other settings.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestDerivedSettings, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
clear_for_tests()
|
||||
self.module = sys.modules[__name__]
|
||||
self.module.SIMPLE_VALUE = 'paneer'
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import httpretty
|
||||
import mock
|
||||
from django.core.cache import cache
|
||||
|
||||
from openedx.core.djangoapps.catalog.models import CatalogIntegration
|
||||
@@ -26,7 +26,7 @@ class TestGetEdxApiData(CatalogIntegrationMixin, CredentialsApiConfigMixin, Cach
|
||||
ENABLED_CACHES = ['default']
|
||||
|
||||
def setUp(self):
|
||||
super(TestGetEdxApiData, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.user = UserFactory()
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ Tests for graph traversal generator functions.
|
||||
from collections import defaultdict
|
||||
from unittest import TestCase
|
||||
|
||||
import six
|
||||
from six.moves import range
|
||||
from ..graph_traversals import traverse_post_order, traverse_pre_order, traverse_topologically
|
||||
|
||||
|
||||
@@ -31,7 +29,7 @@ class TestGraphTraversals(TestCase):
|
||||
# e1 e2
|
||||
# |
|
||||
# f1
|
||||
super(TestGraphTraversals, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.parent_to_children_map = {
|
||||
'a1': ['b1'],
|
||||
'a2': ['b2'],
|
||||
@@ -65,7 +63,7 @@ class TestGraphTraversals(TestCase):
|
||||
will be [].
|
||||
"""
|
||||
result = defaultdict(list)
|
||||
for parent, children in six.iteritems(parent_to_children_map):
|
||||
for parent, children in parent_to_children_map.items():
|
||||
for child in children:
|
||||
result[child].append(parent)
|
||||
return result
|
||||
|
||||
@@ -28,12 +28,12 @@ class RequestUtilTestCase(unittest.TestCase):
|
||||
Tests for request_utils module.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(RequestUtilTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.old_site_name = settings.SITE_NAME
|
||||
self.old_allowed_hosts = settings.ALLOWED_HOSTS
|
||||
|
||||
def tearDown(self):
|
||||
super(RequestUtilTestCase, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().tearDown()
|
||||
settings.SITE_NAME = self.old_site_name
|
||||
settings.ALLOWED_HOSTS = self.old_allowed_hosts
|
||||
|
||||
@@ -43,7 +43,7 @@ class RequestUtilTestCase(unittest.TestCase):
|
||||
that allows us to build an absolute URI.
|
||||
"""
|
||||
stub = get_request_or_stub()
|
||||
expected_url = "http://{site_name}/foobar".format(site_name=settings.SITE_NAME)
|
||||
expected_url = f"http://{settings.SITE_NAME}/foobar"
|
||||
assert stub.build_absolute_uri('foobar') == expected_url
|
||||
|
||||
def test_safe_get_host(self):
|
||||
|
||||
@@ -4,7 +4,6 @@ Tests for Course Teams configuration.
|
||||
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test import TestCase
|
||||
|
||||
from ..teams_config import TeamsConfig, TeamsetConfig, MANAGED_TEAM_MAX_TEAM_SIZE, DEFAULT_COURSE_RUN_MAX_TEAM_SIZE
|
||||
@@ -174,14 +173,14 @@ class TeamsConfigTests(TestCase):
|
||||
Assert that teams configs can be reasonably stringified.
|
||||
"""
|
||||
config = TeamsConfig({})
|
||||
assert six.text_type(config) == "Teams configuration for 0 team-sets"
|
||||
assert str(config) == "Teams configuration for 0 team-sets"
|
||||
|
||||
def test_teamset_config_string(self):
|
||||
"""
|
||||
Assert that team-set configs can be reasonably stringified.
|
||||
"""
|
||||
config = TeamsetConfig({"id": "omlette-du-fromage"})
|
||||
assert six.text_type(config) == "omlette-du-fromage"
|
||||
assert str(config) == "omlette-du-fromage"
|
||||
|
||||
def test_teams_config_repr(self):
|
||||
"""
|
||||
@@ -189,7 +188,7 @@ class TeamsConfigTests(TestCase):
|
||||
"""
|
||||
config = TeamsConfig({"team_sets": [{"id": "hedgehogs"}], "max_team_size": 987})
|
||||
config_repr = repr(config)
|
||||
assert isinstance(config_repr, six.string_types)
|
||||
assert isinstance(config_repr, str)
|
||||
|
||||
# When repr() fails, it doesn't always throw an exception.
|
||||
# Instead, it puts error messages in the repr.
|
||||
|
||||
@@ -17,7 +17,7 @@ class TestTimeZoneUtils(TestCase):
|
||||
"""
|
||||
Sets up user for testing with time zone utils.
|
||||
"""
|
||||
super(TestTimeZoneUtils, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.user = UserFactory.build()
|
||||
self.user.save()
|
||||
|
||||
@@ -4,12 +4,11 @@ Tests for xblock_utils.py
|
||||
|
||||
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.test.client import RequestFactory
|
||||
from mock import patch
|
||||
from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlockAside
|
||||
@@ -41,7 +40,7 @@ class TestXblockUtils(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestXblockUtils, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course_mongo = CourseFactory.create(
|
||||
default_store=ModuleStoreEnum.Type.mongo,
|
||||
org='TestX',
|
||||
@@ -99,7 +98,7 @@ class TestXblockUtils(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Verify that new content is added and the resources are the same.
|
||||
"""
|
||||
fragment = self.create_fragment(u"<h1>Test!</h1>")
|
||||
fragment = self.create_fragment("<h1>Test!</h1>")
|
||||
fragment.initialize_js('BlockMain') # wrap_block() sets some attributes only if there is JS.
|
||||
course = getattr(self, course_id)
|
||||
test_wrap_output = wrap_xblock(
|
||||
@@ -108,7 +107,7 @@ class TestXblockUtils(SharedModuleStoreTestCase):
|
||||
view='baseview',
|
||||
frag=fragment,
|
||||
context={"wrap_xblock_data": {"custom-attribute": "custom-value"}},
|
||||
usage_id_serializer=lambda usage_id: quote_slashes(six.text_type(usage_id)),
|
||||
usage_id_serializer=lambda usage_id: quote_slashes(str(usage_id)),
|
||||
request_token=uuid.uuid1().hex
|
||||
)
|
||||
assert isinstance(test_wrap_output, Fragment)
|
||||
@@ -117,7 +116,7 @@ class TestXblockUtils(SharedModuleStoreTestCase):
|
||||
assert data_usage_id in test_wrap_output.content
|
||||
assert '<h1>Test!</h1>' in test_wrap_output.content
|
||||
assert 'data-custom-attribute="custom-value"' in test_wrap_output.content
|
||||
assert test_wrap_output.resources[0].data == u'body {background-color:red;}'
|
||||
assert test_wrap_output.resources[0].data == 'body {background-color:red;}'
|
||||
assert test_wrap_output.resources[1].data == 'alert("Hi!");'
|
||||
|
||||
@ddt.data('course_mongo', 'course_split')
|
||||
@@ -235,7 +234,7 @@ class TestXBlockAside(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestXBlockAside, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.block = ItemFactory.create(category='aside', parent=cls.course)
|
||||
cls.aside_v2 = AsideUsageKeyV2(cls.block.scope_ids.usage_id, "aside")
|
||||
@@ -254,4 +253,4 @@ class TestXBlockAside(SharedModuleStoreTestCase):
|
||||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside')
|
||||
def test_get_aside(self):
|
||||
"""test get aside success"""
|
||||
assert get_aside_from_xblock(self.block, six.text_type("test_aside")) is not None
|
||||
assert get_aside_from_xblock(self.block, "test_aside") is not None
|
||||
|
||||
@@ -6,10 +6,9 @@ import random
|
||||
import string
|
||||
from collections import namedtuple
|
||||
from unittest import TestCase
|
||||
from unittest import mock
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from six.moves import range
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import NO_CACHE_VALUE, UNIQUE_ID, ScopeIds
|
||||
from xblock.runtime import Runtime
|
||||
@@ -23,7 +22,7 @@ def attribute_pair_repr(self):
|
||||
Custom string representation for the AttributePair namedtuple which is
|
||||
consistent between test runs.
|
||||
"""
|
||||
return u'<AttributePair name={}>'.format(self.name)
|
||||
return f'<AttributePair name={self.name}>'
|
||||
|
||||
|
||||
AttributePair = namedtuple("AttributePair", ["name", "value"])
|
||||
@@ -66,7 +65,7 @@ class DiscussionXBlockImportExportTests(TestCase):
|
||||
"""
|
||||
Set up method
|
||||
"""
|
||||
super(DiscussionXBlockImportExportTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.keys = ScopeIds("any_user", "discussion", "def_id", "usage_id")
|
||||
self.runtime_mock = mock.Mock(spec=Runtime)
|
||||
self.runtime_mock.construct_xblock_from_class = mock.Mock(side_effect=self._construct_xblock_mock)
|
||||
@@ -88,7 +87,7 @@ class DiscussionXBlockImportExportTests(TestCase):
|
||||
"""
|
||||
Test that xblock export XML format can be parsed preserving field values
|
||||
"""
|
||||
xblock_xml = u"""
|
||||
xblock_xml = """
|
||||
<discussion
|
||||
url_name="82bb87a2d22240b1adac2dfcc1e7e5e4" xblock-family="xblock.v1"
|
||||
{id_attr}="{id_value}"
|
||||
@@ -121,7 +120,7 @@ class DiscussionXBlockImportExportTests(TestCase):
|
||||
Test that legacy export XML format can be parsed preserving field values
|
||||
"""
|
||||
xblock_xml = """<discussion url_name="82bb87a2d22240b1adac2dfcc1e7e5e4"/>"""
|
||||
xblock_definition_xml = u"""
|
||||
xblock_definition_xml = """
|
||||
<discussion
|
||||
{id_attr}="{id_value}"
|
||||
{category_attr}="{category_value}"
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Discussion XBlock
|
||||
"""
|
||||
|
||||
import logging
|
||||
import six
|
||||
from six.moves import urllib
|
||||
from six.moves.urllib.parse import urlparse # pylint: disable=import-error
|
||||
import urllib
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import get_language_bidi
|
||||
@@ -180,13 +177,13 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
'enrollment_action': 'enroll',
|
||||
'email_opt_in': False,
|
||||
})
|
||||
login_msg = Text(_(u"You are not signed in. To view the discussion content, {sign_in_link} or "
|
||||
u"{register_link}, and enroll in this course.")).format(
|
||||
sign_in_link=HTML(u'<a href="{url}">{sign_in_label}</a>').format(
|
||||
login_msg = Text(_("You are not signed in. To view the discussion content, {sign_in_link} or "
|
||||
"{register_link}, and enroll in this course.")).format(
|
||||
sign_in_link=HTML('<a href="{url}">{sign_in_label}</a>').format(
|
||||
sign_in_label=_('sign in'),
|
||||
url='{}?{}'.format(reverse('signin_user'), qs),
|
||||
),
|
||||
register_link=HTML(u'<a href="/{url}">{register_label}</a>').format(
|
||||
register_link=HTML('<a href="/{url}">{register_label}</a>').format(
|
||||
register_label=_('register'),
|
||||
url='{}?{}'.format(reverse('register_user'), qs),
|
||||
),
|
||||
@@ -241,7 +238,7 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
XBlock.parse_xml. Otherwise this method parses file in "discussion" folder (known as definition_xml), applies
|
||||
policy.json and updates fields accordingly.
|
||||
"""
|
||||
block = super(DiscussionXBlock, cls).parse_xml(node, runtime, keys, id_generator)
|
||||
block = super().parse_xml(node, runtime, keys, id_generator)
|
||||
|
||||
cls._apply_translations_to_node_attributes(block, node)
|
||||
cls._apply_metadata_and_policy(block, node, runtime)
|
||||
@@ -253,7 +250,7 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
"""
|
||||
Applies metadata translations for attributes stored on an inlined XML element.
|
||||
"""
|
||||
for old_attr, target_attr in six.iteritems(cls.metadata_translations):
|
||||
for old_attr, target_attr in cls.metadata_translations.items():
|
||||
if old_attr in node.attrib and hasattr(block, target_attr):
|
||||
setattr(block, target_attr, node.attrib[old_attr])
|
||||
|
||||
@@ -268,7 +265,7 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
definition_xml, _ = cls.load_definition_xml(node, runtime, block.scope_ids.def_id)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
log.info(
|
||||
u"Exception %s when trying to load definition xml for block %s - assuming XBlock export format",
|
||||
"Exception %s when trying to load definition xml for block %s - assuming XBlock export format",
|
||||
err,
|
||||
block
|
||||
)
|
||||
@@ -277,6 +274,6 @@ class DiscussionXBlock(XBlock, StudioEditableXBlockMixin, XmlParserMixin): # li
|
||||
metadata = cls.load_metadata(definition_xml)
|
||||
cls.apply_policy(metadata, runtime.get_policy(block.scope_ids.usage_id))
|
||||
|
||||
for field_name, value in six.iteritems(metadata):
|
||||
for field_name, value in metadata.items():
|
||||
if field_name in block.fields:
|
||||
setattr(block, field_name, value)
|
||||
|
||||
@@ -27,7 +27,7 @@ class XBlockPackageStorage(Storage):
|
||||
"""
|
||||
Returns a static file storage if available in the given app.
|
||||
"""
|
||||
super(XBlockPackageStorage, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
self.module = module
|
||||
self.base_dir = base_dir
|
||||
|
||||
@@ -119,7 +119,7 @@ class XBlockPipelineFinder(BaseFinder): # lint-amnesty, pylint: disable=abstrac
|
||||
initialization happens, we just proxy all list()/find() requests by
|
||||
iterating through the XBlockPackageStorage objects.
|
||||
"""
|
||||
super(XBlockPipelineFinder, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# xblock_resource_info holds (package_name, resources_dir) tuples. While
|
||||
# it never happens in practice, the XBlock API does allow different
|
||||
|
||||
@@ -11,7 +11,6 @@ import re
|
||||
import uuid
|
||||
|
||||
import markupsafe
|
||||
import six
|
||||
import webpack_loader.utils
|
||||
from contracts import contract
|
||||
from django.conf import settings
|
||||
@@ -23,7 +22,6 @@ from edx_django_utils.plugins import pluggable_override
|
||||
from lxml import etree, html
|
||||
from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock
|
||||
from xblock.exceptions import InvalidScopeError
|
||||
@@ -149,8 +147,8 @@ def wrap_xblock(
|
||||
'content': block.display_name if display_name_only else frag.content,
|
||||
'classes': css_classes,
|
||||
'display_name': block.display_name_with_default_escaped, # xss-lint: disable=python-deprecated-display-name
|
||||
'data_attributes': u' '.join(u'data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
|
||||
for key, value in six.iteritems(data)),
|
||||
'data_attributes': ' '.join('data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
|
||||
for key, value in data.items()),
|
||||
}
|
||||
|
||||
if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:
|
||||
@@ -222,8 +220,8 @@ def wrap_xblock_aside(
|
||||
template_context = {
|
||||
'content': frag.content,
|
||||
'classes': css_classes,
|
||||
'data_attributes': u' '.join(u'data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
|
||||
for key, value in six.iteritems(data)),
|
||||
'data_attributes': ' '.join('data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
|
||||
for key, value in data.items()),
|
||||
}
|
||||
|
||||
if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:
|
||||
@@ -287,14 +285,14 @@ def grade_histogram(module_id):
|
||||
from django.db import connection
|
||||
cursor = connection.cursor()
|
||||
|
||||
query = u"""\
|
||||
query = """\
|
||||
SELECT courseware_studentmodule.grade,
|
||||
COUNT(courseware_studentmodule.student_id)
|
||||
FROM courseware_studentmodule
|
||||
WHERE courseware_studentmodule.module_id=%s
|
||||
GROUP BY courseware_studentmodule.grade"""
|
||||
# Passing module_id this way prevents sql-injection.
|
||||
cursor.execute(query, [text_type(module_id)])
|
||||
cursor.execute(query, [str(module_id)])
|
||||
|
||||
grades = list(cursor.fetchall())
|
||||
grades.sort(key=lambda x: x[0]) # Add ORDER BY to sql query?
|
||||
@@ -311,7 +309,7 @@ def sanitize_html_id(html_id):
|
||||
return sanitized_html_id
|
||||
|
||||
|
||||
@contract(user=User, block=XBlock, view=six.string_types[0], frag=Fragment, context="dict|None")
|
||||
@contract(user=User, block=XBlock, view=(str,)[0], frag=Fragment, context="dict|None")
|
||||
def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context): # pylint: disable=unused-argument
|
||||
"""
|
||||
Updates the supplied module with a new get_html function that wraps
|
||||
@@ -349,7 +347,7 @@ def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context)
|
||||
filepath = filename
|
||||
data_dir = block.static_asset_path or osfs.root_path.rsplit('/')[-1]
|
||||
giturl = block.giturl or 'https://github.com/MITx'
|
||||
edit_link = "%s/%s/tree/master/%s" % (giturl, data_dir, filepath)
|
||||
edit_link = f"{giturl}/{data_dir}/tree/master/{filepath}"
|
||||
else:
|
||||
edit_link = False
|
||||
# Need to define all the variables that are about to be used
|
||||
@@ -383,7 +381,7 @@ def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context)
|
||||
'location': block.location,
|
||||
'xqa_key': block.xqa_key,
|
||||
'source_file': source_file,
|
||||
'source_url': '%s/%s/tree/master/%s' % (giturl, data_dir, source_file),
|
||||
'source_url': f'{giturl}/{data_dir}/tree/master/{source_file}',
|
||||
'category': str(block.__class__.__name__),
|
||||
'element_id': sanitize_html_id(block.location.html_id()),
|
||||
'edit_link': edit_link,
|
||||
@@ -550,7 +548,7 @@ def hash_resource(resource):
|
||||
for data in resource:
|
||||
if isinstance(data, bytes):
|
||||
md5.update(data)
|
||||
elif isinstance(data, six.string_types):
|
||||
elif isinstance(data, str):
|
||||
md5.update(data.encode('utf-8'))
|
||||
else:
|
||||
md5.update(repr(data).encode('utf-8'))
|
||||
|
||||
@@ -17,7 +17,7 @@ class TestAdminView(TestCase):
|
||||
Tests of the admin view
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestAdminView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.client = Client()
|
||||
|
||||
def test_admin_view_loads_for_is_staff(self):
|
||||
|
||||
Reference in New Issue
Block a user