Skip to content
Snippets Groups Projects
Commit 068ac529 authored by Usama Sadiq's avatar Usama Sadiq
Browse files

refactor: Ran pyupgrade on openedx/core

Ran pyupgrade on openedx/core/{lib, tests}
parent 8de47ef5
No related branches found
Tags release-2021-04-08-11.08
No related merge requests found
Showing
with 98 additions and 104 deletions
......@@ -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:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment