diff --git a/openedx/core/lib/api/authentication.py b/openedx/core/lib/api/authentication.py index 9740b38521..a762d398b3 100644 --- a/openedx/core/lib/api/authentication.py +++ b/openedx/core/lib/api/authentication.py @@ -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 diff --git a/openedx/core/lib/api/fields.py b/openedx/core/lib/api/fields.py index 411b4cf014..0f8e5ddf18 100644 --- a/openedx/core/lib/api/fields.py +++ b/openedx/core/lib/api/fields.py @@ -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:')): diff --git a/openedx/core/lib/api/mixins.py b/openedx/core/lib/api/mixins.py index b2cddc9439..f0af9072e1 100644 --- a/openedx/core/lib/api/mixins.py +++ b/openedx/core/lib/api/mixins.py @@ -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 diff --git a/openedx/core/lib/api/parsers.py b/openedx/core/lib/api/parsers.py index 6225e80475..fc42aab8b8 100644 --- a/openedx/core/lib/api/parsers.py +++ b/openedx/core/lib/api/parsers.py @@ -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): diff --git a/openedx/core/lib/api/serializers.py b/openedx/core/lib/api/serializers.py index 3563f2cc6e..c620599d50 100644 --- a/openedx/core/lib/api/serializers.py +++ b/openedx/core/lib/api/serializers.py @@ -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 diff --git a/openedx/core/lib/api/test_utils.py b/openedx/core/lib/api/test_utils.py index 91669f1e1f..20a26ea4a5 100644 --- a/openedx/core/lib/api/test_utils.py +++ b/openedx/core/lib/api/test_utils.py @@ -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 diff --git a/openedx/core/lib/api/tests/mixins.py b/openedx/core/lib/api/tests/mixins.py index c1018bd004..21da4509ec 100644 --- a/openedx/core/lib/api/tests/mixins.py +++ b/openedx/core/lib/api/tests/mixins.py @@ -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 '' diff --git a/openedx/core/lib/api/tests/test_authentication.py b/openedx/core/lib/api/tests/test_authentication.py index 4b5926ad9a..2d5ddde480 100644 --- a/openedx/core/lib/api/tests/test_authentication.py +++ b/openedx/core/lib/api/tests/test_authentication.py @@ -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() diff --git a/openedx/core/lib/api/tests/test_exceptions.py b/openedx/core/lib/api/tests/test_exceptions.py index e2e8181b5d..36d765fe8a 100644 --- a/openedx/core/lib/api/tests/test_exceptions.py +++ b/openedx/core/lib/api/tests/test_exceptions.py @@ -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'] diff --git a/openedx/core/lib/api/tests/test_fields.py b/openedx/core/lib/api/tests/test_fields.py index 9ea952c124..f23756fb0e 100644 --- a/openedx/core/lib/api/tests/test_fields.py +++ b/openedx/core/lib/api/tests/test_fields.py @@ -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 diff --git a/openedx/core/lib/api/tests/test_parsers.py b/openedx/core/lib/api/tests/test_parsers.py index f2ace6b622..f9af1e850c 100644 --- a/openedx/core/lib/api/tests/test_parsers.py +++ b/openedx/core/lib/api/tests/test_parsers.py @@ -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'} diff --git a/openedx/core/lib/api/tests/test_permissions.py b/openedx/core/lib/api/tests/test_permissions.py index 38bf8ceeea..ae114522c8 100644 --- a/openedx/core/lib/api/tests/test_permissions.py +++ b/openedx/core/lib/api/tests/test_permissions.py @@ -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) diff --git a/openedx/core/lib/api/tests/test_view_utils.py b/openedx/core/lib/api/tests/test_view_utils.py index ac3c577937..be7a55ee2d 100644 --- a/openedx/core/lib/api/tests/test_view_utils.py +++ b/openedx/core/lib/api/tests/test_view_utils.py @@ -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.*)/$', 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 diff --git a/openedx/core/lib/api/view_utils.py b/openedx/core/lib/api/view_utils.py index bcc395a763..8ceaae7e0e 100644 --- a/openedx/core/lib/api/view_utils.py +++ b/openedx/core/lib/api/view_utils.py @@ -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' ) diff --git a/openedx/core/lib/blockstore_api/methods.py b/openedx/core/lib/blockstore_api/methods.py index 253aa42f93..7d14fae517 100644 --- a/openedx/core/lib/blockstore_api/methods.py +++ b/openedx/core/lib/blockstore_api/methods.py @@ -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 diff --git a/openedx/core/lib/blockstore_api/models.py b/openedx/core/lib/blockstore_api/models.py index 995571e2ca..8f2127ca90 100644 --- a/openedx/core/lib/blockstore_api/models.py +++ b/openedx/core/lib/blockstore_api/models.py @@ -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 """ diff --git a/openedx/core/lib/celery/routers.py b/openedx/core/lib/celery/routers.py index acc3434ebb..3f61bf65bf 100644 --- a/openedx/core/lib/celery/routers.py +++ b/openedx/core/lib/celery/routers.py @@ -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 ) diff --git a/openedx/core/lib/gating/api.py b/openedx/core/lib/gating/api.py index 4133116b5c..313a326756 100644 --- a/openedx/core/lib/gating/api.py +++ b/openedx/core/lib/gating/api.py @@ -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 diff --git a/openedx/core/lib/gating/services.py b/openedx/core/lib/gating/services.py index a1f7392ad3..901ee1ad9a 100644 --- a/openedx/core/lib/gating/services.py +++ b/openedx/core/lib/gating/services.py @@ -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. """ diff --git a/openedx/core/lib/gating/tests/test_api.py b/openedx/core/lib/gating/tests/test_api.py index 61a94cd2d9..d26288207b 100644 --- a/openedx/core/lib/gating/tests/test_api.py +++ b/openedx/core/lib/gating/tests/test_api.py @@ -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: diff --git a/openedx/core/lib/tests/assertions/events.py b/openedx/core/lib/tests/assertions/events.py index 1293eb6271..1aeaeb36d5 100644 --- a/openedx/core/lib/tests/assertions/events.py +++ b/openedx/core/lib/tests/assertions/events.py @@ -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) diff --git a/openedx/core/lib/tests/test_cache_utils.py b/openedx/core/lib/tests/test_cache_utils.py index b6e40a94fc..2d051db517 100644 --- a/openedx/core/lib/tests/test_cache_utils.py +++ b/openedx/core/lib/tests/test_cache_utils.py @@ -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. diff --git a/openedx/core/lib/tests/test_course_tabs.py b/openedx/core/lib/tests/test_course_tabs.py index 2517251751..b3b8e33c84 100644 --- a/openedx/core/lib/tests/test_course_tabs.py +++ b/openedx/core/lib/tests/test_course_tabs.py @@ -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} diff --git a/openedx/core/lib/tests/test_courses.py b/openedx/core/lib/tests/test_courses.py index aacbeae30f..5bbe831e59 100644 --- a/openedx/core/lib/tests/test_courses.py +++ b/openedx/core/lib/tests/test_courses.py @@ -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') ) diff --git a/openedx/core/lib/tests/test_derived.py b/openedx/core/lib/tests/test_derived.py index 8de4e0ae51..ef3f980424 100644 --- a/openedx/core/lib/tests/test_derived.py +++ b/openedx/core/lib/tests/test_derived.py @@ -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' diff --git a/openedx/core/lib/tests/test_edx_api_utils.py b/openedx/core/lib/tests/test_edx_api_utils.py index e09a322a92..e07af3591a 100644 --- a/openedx/core/lib/tests/test_edx_api_utils.py +++ b/openedx/core/lib/tests/test_edx_api_utils.py @@ -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() diff --git a/openedx/core/lib/tests/test_graph_traversals.py b/openedx/core/lib/tests/test_graph_traversals.py index ce86b4be06..47996ccde3 100644 --- a/openedx/core/lib/tests/test_graph_traversals.py +++ b/openedx/core/lib/tests/test_graph_traversals.py @@ -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 diff --git a/openedx/core/lib/tests/test_request_utils.py b/openedx/core/lib/tests/test_request_utils.py index 9e94764105..23f60c8401 100644 --- a/openedx/core/lib/tests/test_request_utils.py +++ b/openedx/core/lib/tests/test_request_utils.py @@ -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): diff --git a/openedx/core/lib/tests/test_teams_config.py b/openedx/core/lib/tests/test_teams_config.py index e17e0e3b4b..a088c45bc2 100644 --- a/openedx/core/lib/tests/test_teams_config.py +++ b/openedx/core/lib/tests/test_teams_config.py @@ -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. diff --git a/openedx/core/lib/tests/test_time_zone_utils.py b/openedx/core/lib/tests/test_time_zone_utils.py index cfab630cc0..c876f68be2 100644 --- a/openedx/core/lib/tests/test_time_zone_utils.py +++ b/openedx/core/lib/tests/test_time_zone_utils.py @@ -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() diff --git a/openedx/core/lib/tests/test_xblock_utils.py b/openedx/core/lib/tests/test_xblock_utils.py index 7be7eedf16..1bd926c648 100644 --- a/openedx/core/lib/tests/test_xblock_utils.py +++ b/openedx/core/lib/tests/test_xblock_utils.py @@ -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"

Test!

") + fragment = self.create_fragment("

Test!

") 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 '

Test!

' 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 diff --git a/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py b/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py index ce20b4d81e..0779216d43 100644 --- a/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py +++ b/openedx/core/lib/xblock_builtin/xblock_discussion/tests.py @@ -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''.format(self.name) + return f'' 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 = """ """ - xblock_definition_xml = u""" + xblock_definition_xml = """ {sign_in_label}').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('{sign_in_label}').format( sign_in_label=_('sign in'), url='{}?{}'.format(reverse('signin_user'), qs), ), - register_link=HTML(u'{register_label}').format( + register_link=HTML('{register_label}').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) diff --git a/openedx/core/lib/xblock_pipeline/finder.py b/openedx/core/lib/xblock_pipeline/finder.py index 3054029761..da5f4e5330 100644 --- a/openedx/core/lib/xblock_pipeline/finder.py +++ b/openedx/core/lib/xblock_pipeline/finder.py @@ -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 diff --git a/openedx/core/lib/xblock_utils/__init__.py b/openedx/core/lib/xblock_utils/__init__.py index 288a995301..0eaa4f437f 100644 --- a/openedx/core/lib/xblock_utils/__init__.py +++ b/openedx/core/lib/xblock_utils/__init__.py @@ -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')) diff --git a/openedx/core/tests/test_admin_view.py b/openedx/core/tests/test_admin_view.py index d6275b464e..5e84fd0012 100644 --- a/openedx/core/tests/test_admin_view.py +++ b/openedx/core/tests/test_admin_view.py @@ -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):