From e9f9fcb17bb70f639ddaf4c1f1dc535ff19d2634 Mon Sep 17 00:00:00 2001 From: Usama Sadiq Date: Thu, 11 Mar 2021 17:24:52 +0500 Subject: [PATCH] refactor: Ran pyupgrade on openedx/core/djangoapps/user_api Co-authored-by: Muhammad Soban Javed <58461728+iamsobanjaved@users.noreply.github.com> --- .../tests/test_content_highlights.py | 4 +- .../djangoapps/user_api/accounts/__init__.py | 46 +- .../core/djangoapps/user_api/accounts/api.py | 53 +- .../djangoapps/user_api/accounts/forms.py | 4 +- .../user_api/accounts/image_helpers.py | 4 +- .../user_api/accounts/serializers.py | 43 +- .../user_api/accounts/settings_views.py | 28 +- .../djangoapps/user_api/accounts/utils.py | 12 +- .../djangoapps/user_api/accounts/views.py | 63 ++- openedx/core/djangoapps/user_api/admin.py | 18 +- .../core/djangoapps/user_api/config/waffle.py | 2 +- .../djangoapps/user_api/course_tag/api.py | 6 +- openedx/core/djangoapps/user_api/errors.py | 4 +- openedx/core/djangoapps/user_api/helpers.py | 34 +- .../core/djangoapps/user_api/legacy_urls.py | 2 +- .../core/djangoapps/user_api/message_types.py | 2 +- .../user_api/migrations/0001_initial.py | 11 +- ...02_retirementstate_userretirementstatus.py | 1 - .../migrations/0003_userretirementrequest.py | 1 - ...04_userretirementpartnerreportingstatus.py | 1 - openedx/core/djangoapps/user_api/models.py | 55 +- .../djangoapps/user_api/partition_schemes.py | 12 +- .../djangoapps/user_api/preferences/api.py | 35 +- .../core/djangoapps/user_api/serializers.py | 8 +- .../djangoapps/user_api/tests/factories.py | 6 +- .../user_api/tests/test_constants.py | 501 +++++++++--------- .../djangoapps/user_api/tests/test_helpers.py | 21 +- .../user_api/tests/test_middleware.py | 6 +- .../djangoapps/user_api/tests/test_models.py | 2 +- .../user_api/tests/test_partition_schemes.py | 9 +- .../djangoapps/user_api/tests/test_views.py | 64 ++- openedx/core/djangoapps/user_api/urls.py | 18 +- openedx/core/djangoapps/user_api/views.py | 3 +- 33 files changed, 525 insertions(+), 554 deletions(-) diff --git a/openedx/core/djangoapps/schedules/tests/test_content_highlights.py b/openedx/core/djangoapps/schedules/tests/test_content_highlights.py index fb2210cdb9..c38b888e1f 100644 --- a/openedx/core/djangoapps/schedules/tests/test_content_highlights.py +++ b/openedx/core/djangoapps/schedules/tests/test_content_highlights.py @@ -147,7 +147,9 @@ class TestContentHighlights(ModuleStoreTestCase): # lint-amnesty, pylint: disab assert get_next_section_highlights(self.user, self.course_key, two_days_ago, today.date()) ==\ (['skipped a week'], 2) - exception_message = 'Next section [{}] has no highlights for {}'.format('chapter 3', self.course_key) + exception_message = 'Next section [{}] has no highlights for {}'.format( # pylint: disable=unused-variable + 'chapter 3', self.course_key + ) with pytest.raises(CourseUpdateDoesNotExist): get_next_section_highlights(self.user, self.course_key, two_days_ago, two_days.date()) # Returns None, None if the target date does not match any due dates. This is caused by diff --git a/openedx/core/djangoapps/user_api/accounts/__init__.py b/openedx/core/djangoapps/user_api/accounts/__init__.py index 1cced0694b..04080e1776 100644 --- a/openedx/core/djangoapps/user_api/accounts/__init__.py +++ b/openedx/core/djangoapps/user_api/accounts/__init__.py @@ -40,58 +40,58 @@ VISIBILITY_PREFIX = 'visibility.' # It is shown to users who attempt to create a new account using invalid characters # in the username. USERNAME_INVALID_CHARS_ASCII = _( - u"Usernames can only contain letters (A-Z, a-z), numerals (0-9), underscores (_), and hyphens (-)." + "Usernames can only contain letters (A-Z, a-z), numerals (0-9), underscores (_), and hyphens (-)." ) # Translators: This message is shown only when the Unicode usernames are allowed. # It is shown to users who attempt to create a new account using invalid characters # in the username. USERNAME_INVALID_CHARS_UNICODE = _( - u"Usernames can only contain letters, numerals, and @/./+/-/_ characters." + "Usernames can only contain letters, numerals, and @/./+/-/_ characters." ) # Translators: This message is shown to users who attempt to create a new account using # an invalid email format. -EMAIL_INVALID_MSG = _(u'"{email}" is not a valid email address.') +EMAIL_INVALID_MSG = _('"{email}" is not a valid email address.') # Translators: This message is shown to users who attempt to create a new # account using an username/email associated with an existing account. EMAIL_CONFLICT_MSG = _( - u"It looks like {email_address} belongs to an existing account. " - u"Try again with a different email address." + "It looks like {email_address} belongs to an existing account. " + "Try again with a different email address." ) USERNAME_CONFLICT_MSG = _( - u"It looks like {username} belongs to an existing account. " - u"Try again with a different username." + "It looks like {username} belongs to an existing account. " + "Try again with a different username." ) # Translators: This message is shown to users who enter a username/email/password # with an inappropriate length (too short or too long). USERNAME_BAD_LENGTH_MSG = format_lazy( - _(u"Username must be between {min} and {max} characters long."), + _("Username must be between {min} and {max} characters long."), min=USERNAME_MIN_LENGTH, max=USERNAME_MAX_LENGTH, ) EMAIL_BAD_LENGTH_MSG = format_lazy( - _(u"Enter a valid email address that contains at least {min} characters."), + _("Enter a valid email address that contains at least {min} characters."), min=EMAIL_MIN_LENGTH, ) # These strings are normally not user-facing. -USERNAME_BAD_TYPE_MSG = u"Username must be a string." -EMAIL_BAD_TYPE_MSG = u"Email must be a string." -PASSWORD_BAD_TYPE_MSG = u"Password must be a string." +USERNAME_BAD_TYPE_MSG = "Username must be a string." +EMAIL_BAD_TYPE_MSG = "Email must be a string." +PASSWORD_BAD_TYPE_MSG = "Password must be a string." # Translators: These messages are shown to users who do not enter information # into the required field or enter it incorrectly. -REQUIRED_FIELD_NAME_MSG = _(u"Enter your full name.") -REQUIRED_FIELD_CONFIRM_EMAIL_MSG = _(u"The email addresses do not match.") -REQUIRED_FIELD_COUNTRY_MSG = _(u"Select your country or region of residence.") -REQUIRED_FIELD_PROFESSION_SELECT_MSG = _(u"Select your profession.") -REQUIRED_FIELD_SPECIALTY_SELECT_MSG = _(u"Select your specialty.") -REQUIRED_FIELD_PROFESSION_TEXT_MSG = _(u"Enter your profession.") -REQUIRED_FIELD_SPECIALTY_TEXT_MSG = _(u"Enter your specialty.") -REQUIRED_FIELD_CITY_MSG = _(u"Enter your city.") -REQUIRED_FIELD_GOALS_MSG = _(u"Tell us your goals.") -REQUIRED_FIELD_LEVEL_OF_EDUCATION_MSG = _(u"Select the highest level of education you have completed.") -REQUIRED_FIELD_MAILING_ADDRESS_MSG = _(u"Enter your mailing address.") +REQUIRED_FIELD_NAME_MSG = _("Enter your full name.") +REQUIRED_FIELD_CONFIRM_EMAIL_MSG = _("The email addresses do not match.") +REQUIRED_FIELD_COUNTRY_MSG = _("Select your country or region of residence.") +REQUIRED_FIELD_PROFESSION_SELECT_MSG = _("Select your profession.") +REQUIRED_FIELD_SPECIALTY_SELECT_MSG = _("Select your specialty.") +REQUIRED_FIELD_PROFESSION_TEXT_MSG = _("Enter your profession.") +REQUIRED_FIELD_SPECIALTY_TEXT_MSG = _("Enter your specialty.") +REQUIRED_FIELD_CITY_MSG = _("Enter your city.") +REQUIRED_FIELD_GOALS_MSG = _("Tell us your goals.") +REQUIRED_FIELD_LEVEL_OF_EDUCATION_MSG = _("Select the highest level of education you have completed.") +REQUIRED_FIELD_MAILING_ADDRESS_MSG = _("Enter your mailing address.") diff --git a/openedx/core/djangoapps/user_api/accounts/api.py b/openedx/core/djangoapps/user_api/accounts/api.py index cd2bd027d5..6713098ce2 100644 --- a/openedx/core/djangoapps/user_api/accounts/api.py +++ b/openedx/core/djangoapps/user_api/accounts/api.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # pylint: disable=missing-docstring """ Programmatic integration point for User API Accounts sub-application @@ -7,14 +6,12 @@ Programmatic integration point for User API Accounts sub-application import datetime -import six from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.core.validators import ValidationError, validate_email from django.utils.translation import override as override_language from django.utils.translation import ugettext as _ from pytz import UTC -from six import text_type # pylint: disable=ungrouped-imports from common.djangoapps.student import views as student_views from common.djangoapps.student.models import ( AccountRecovery, @@ -166,7 +163,7 @@ def update_account_settings(requesting_user, update, username=None): raise err except Exception as err: raise AccountUpdateError( # lint-amnesty, pylint: disable=raise-missing-from - u"Error thrown when saving account updates: '{}'".format(text_type(err)) + "Error thrown when saving account updates: '{}'".format(str(err)) ) _send_email_change_requests_if_needed(update, user) @@ -176,15 +173,15 @@ def _validate_read_only_fields(user, data, field_errors): # Check for fields that are not editable. Marking them read-only causes them to be ignored, but we wish to 400. read_only_fields = set(data.keys()).intersection( # Remove email since it is handled separately below when checking for changing_email. - (set(AccountUserSerializer.get_read_only_fields()) - set(["email"])) | + (set(AccountUserSerializer.get_read_only_fields()) - {"email"}) | set(AccountLegacyProfileSerializer.get_read_only_fields() or set()) | get_enterprise_readonly_account_fields(user) ) for read_only_field in read_only_fields: field_errors[read_only_field] = { - "developer_message": u"This field is not editable via this API", - "user_message": _(u"The '{field_name}' field cannot be edited.").format(field_name=read_only_field) + "developer_message": "This field is not editable via this API", + "user_message": _("The '{field_name}' field cannot be edited.").format(field_name=read_only_field) } del data[read_only_field] @@ -196,15 +193,15 @@ def _validate_email_change(user, data, field_errors): return if not settings.FEATURES['ALLOW_EMAIL_ADDRESS_CHANGE']: - raise AccountUpdateError(u"Email address changes have been disabled by the site operators.") + raise AccountUpdateError("Email address changes have been disabled by the site operators.") new_email = data["email"] try: student_views.validate_new_email(user, new_email) except ValueError as err: field_errors["email"] = { - "developer_message": u"Error thrown from validate_new_email: '{}'".format(text_type(err)), - "user_message": text_type(err) + "developer_message": "Error thrown from validate_new_email: '{}'".format(str(err)), + "user_message": str(err) } return @@ -225,8 +222,8 @@ def _validate_secondary_email(user, data, field_errors): student_views.validate_secondary_email(user, secondary_email) except ValueError as err: field_errors["secondary_email"] = { - "developer_message": u"Error thrown from validate_secondary_email: '{}'".format(text_type(err)), - "user_message": text_type(err) + "developer_message": "Error thrown from validate_secondary_email: '{}'".format(str(err)), + "user_message": str(err) } else: # Don't process with sending email to given new email, if it is already associated with @@ -247,7 +244,7 @@ def _validate_name_change(user_profile, data, field_errors): validate_name(data['name']) except ValidationError as err: field_errors["name"] = { - "developer_message": u"Error thrown from validate_name: '{}'".format(err.message), + "developer_message": f"Error thrown from validate_name: '{err.message}'", "user_message": err.message } return None @@ -310,7 +307,7 @@ def _store_old_name_if_needed(old_name, user_profile, requesting_user): meta['old_names'] = [] meta['old_names'].append([ old_name, - u"Name change requested through account API by {0}".format(requesting_user.username), + f"Name change requested through account API by {requesting_user.username}", datetime.datetime.now(UTC).isoformat() ]) user_profile.set_meta(meta) @@ -324,8 +321,8 @@ def _send_email_change_requests_if_needed(data, user): student_views.do_email_change_request(user, new_email) except ValueError as err: raise AccountUpdateError( # lint-amnesty, pylint: disable=raise-missing-from - u"Error thrown from do_email_change_request: '{}'".format(text_type(err)), - user_message=text_type(err) + "Error thrown from do_email_change_request: '{}'".format(str(err)), + user_message=str(err) ) new_secondary_email = data.get("secondary_email") @@ -338,8 +335,8 @@ def _send_email_change_requests_if_needed(data, user): ) except ValueError as err: raise AccountUpdateError( # lint-amnesty, pylint: disable=raise-missing-from - u"Error thrown from do_email_change_request: '{}'".format(text_type(err)), - user_message=text_type(err) + "Error thrown from do_email_change_request: '{}'".format(str(err)), + user_message=str(err) ) @@ -480,7 +477,7 @@ def _validate(validation_func, err, *args): try: validation_func(*args) except err as validation_err: - return text_type(validation_err) + return str(validation_err) return '' @@ -499,7 +496,7 @@ def _validate_username(username): """ try: _validate_unicode(username) - _validate_type(username, six.string_types, accounts.USERNAME_BAD_TYPE_MSG) + _validate_type(username, str, accounts.USERNAME_BAD_TYPE_MSG) _validate_length( username, accounts.USERNAME_MIN_LENGTH, @@ -511,7 +508,7 @@ def _validate_username(username): # message by convention. validate_username(username) except (UnicodeError, errors.AccountDataBadType, errors.AccountDataBadLength) as username_err: - raise errors.AccountUsernameInvalid(text_type(username_err)) + raise errors.AccountUsernameInvalid(str(username_err)) except ValidationError as validation_err: raise errors.AccountUsernameInvalid(validation_err.message) @@ -531,12 +528,12 @@ def _validate_email(email): """ try: _validate_unicode(email) - _validate_type(email, six.string_types, accounts.EMAIL_BAD_TYPE_MSG) + _validate_type(email, str, accounts.EMAIL_BAD_TYPE_MSG) _validate_length(email, accounts.EMAIL_MIN_LENGTH, accounts.EMAIL_MAX_LENGTH, accounts.EMAIL_BAD_LENGTH_MSG) validate_email.message = accounts.EMAIL_INVALID_MSG.format(email=email) validate_email(email) except (UnicodeError, errors.AccountDataBadType, errors.AccountDataBadLength) as invalid_email_err: - raise errors.AccountEmailInvalid(text_type(invalid_email_err)) + raise errors.AccountEmailInvalid(str(invalid_email_err)) except ValidationError as validation_err: raise errors.AccountEmailInvalid(validation_err.message) @@ -573,11 +570,11 @@ def _validate_password(password, username=None, email=None): """ try: - _validate_type(password, six.string_types, accounts.PASSWORD_BAD_TYPE_MSG) + _validate_type(password, str, accounts.PASSWORD_BAD_TYPE_MSG) temp_user = User(username=username, email=email) if username else None validate_password(password, user=temp_user) except errors.AccountDataBadType as invalid_password_err: - raise errors.AccountPasswordInvalid(text_type(invalid_password_err)) + raise errors.AccountPasswordInvalid(str(invalid_password_err)) except ValidationError as validation_err: raise errors.AccountPasswordInvalid(' '.join(validation_err.messages)) @@ -680,7 +677,7 @@ def _validate_length(data, min, max, err): # lint-amnesty, pylint: disable=rede raise errors.AccountDataBadLength(err) -def _validate_unicode(data, err=u"Input not valid unicode"): +def _validate_unicode(data, err="Input not valid unicode"): """Checks whether the input data is valid unicode or not. :param data: The data to check for unicode validity. @@ -690,9 +687,9 @@ def _validate_unicode(data, err=u"Input not valid unicode"): """ try: - if not isinstance(data, str) and not isinstance(data, six.text_type): + if not isinstance(data, str) and not isinstance(data, str): raise UnicodeError(err) # In some cases we pass the above, but it's still inappropriate utf-8. - six.text_type(data) + str(data) except UnicodeError: raise UnicodeError(err) # lint-amnesty, pylint: disable=raise-missing-from diff --git a/openedx/core/djangoapps/user_api/accounts/forms.py b/openedx/core/djangoapps/user_api/accounts/forms.py index bce342e948..3efed24e23 100644 --- a/openedx/core/djangoapps/user_api/accounts/forms.py +++ b/openedx/core/djangoapps/user_api/accounts/forms.py @@ -26,8 +26,8 @@ class RetirementQueueDeletionForm(forms.Form): None, # Translators: 'current_state' is a string from an enumerated list indicating the learner's retirement # state. Example: FORUMS_COMPLETE - u"Retirement requests can only be cancelled for users in the PENDING state." - u" Current request state for '{original_username}': {current_state}".format( + "Retirement requests can only be cancelled for users in the PENDING state." + " Current request state for '{original_username}': {current_state}".format( original_username=retirement.original_username, current_state=retirement.current_state.state_name ) diff --git a/openedx/core/djangoapps/user_api/accounts/image_helpers.py b/openedx/core/djangoapps/user_api/accounts/image_helpers.py index a825e67060..43aa4a60ae 100644 --- a/openedx/core/djangoapps/user_api/accounts/image_helpers.py +++ b/openedx/core/djangoapps/user_api/accounts/image_helpers.py @@ -43,7 +43,7 @@ def _get_profile_image_filename(name, size, file_extension=PROFILE_IMAGE_FILE_EX """ Returns the full filename for a profile image, given the name and size. """ - return '{name}_{size}.{file_extension}'.format(name=name, size=size, file_extension=file_extension) + return f'{name}_{size}.{file_extension}' def _get_profile_image_urls(name, storage, file_extension=PROFILE_IMAGE_FILE_EXTENSION, version=None): @@ -60,7 +60,7 @@ def _get_profile_image_urls(name, storage, file_extension=PROFILE_IMAGE_FILE_EXT # query string (such as signed S3 URLs), append to the query # string with "&v=" instead. separator = '&' if '?' in url else '?' - return '{}{}v={}'.format(url, separator, version) if version is not None else url + return f'{url}{separator}v={version}' if version is not None else url return {size_display_name: _make_url(size) for size_display_name, size in settings.PROFILE_IMAGE_SIZES_MAP.items()} diff --git a/openedx/core/djangoapps/user_api/accounts/serializers.py b/openedx/core/djangoapps/user_api/accounts/serializers.py index 695b48306e..7dcf679b75 100644 --- a/openedx/core/djangoapps/user_api/accounts/serializers.py +++ b/openedx/core/djangoapps/user_api/accounts/serializers.py @@ -12,7 +12,6 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse from rest_framework import serializers -from six import text_type from common.djangoapps.student.models import UserPasswordToggleHistory from lms.djangoapps.badges.utils import badges_enabled @@ -54,7 +53,7 @@ class LanguageProficiencySerializer(serializers.ModelSerializer): Class that serializes the LanguageProficiency model for account information. """ - class Meta(object): + class Meta: model = LanguageProficiency fields = ("code",) @@ -75,7 +74,7 @@ class SocialLinkSerializer(serializers.ModelSerializer): """ Class that serializes the SocialLink model for the UserProfile object. """ - class Meta(object): + class Meta: model = SocialLink fields = ("platform", "social_link") @@ -86,7 +85,7 @@ class SocialLinkSerializer(serializers.ModelSerializer): valid_platforms = ["facebook", "twitter", "linkedin"] if platform not in valid_platforms: raise serializers.ValidationError( - u"The social platform must be facebook, twitter or linkedin" + "The social platform must be facebook, twitter or linkedin" ) return platform @@ -105,7 +104,7 @@ class UserReadOnlySerializer(serializers.Serializer): # lint-amnesty, pylint: d # Don't pass the 'custom_fields' arg up to the superclass self.custom_fields = kwargs.pop('custom_fields', []) - super(UserReadOnlySerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) def to_representation(self, user): # lint-amnesty, pylint: disable=arguments-differ """ @@ -117,7 +116,7 @@ class UserReadOnlySerializer(serializers.Serializer): # lint-amnesty, pylint: d user_profile = user.profile except ObjectDoesNotExist: user_profile = None - LOGGER.warning(u"user profile for the user [%s] does not exist", user.username) + LOGGER.warning("user profile for the user [%s] does not exist", user.username) try: account_recovery = user.account_recovery @@ -226,7 +225,7 @@ class UserAccountDisableHistorySerializer(serializers.ModelSerializer): """ created_by = serializers.SerializerMethodField() - class Meta(object): + class Meta: model = UserPasswordToggleHistory fields = ("created", "comment", "disabled", "created_by") @@ -240,7 +239,7 @@ class AccountUserSerializer(serializers.HyperlinkedModelSerializer, ReadOnlyFiel """ password_toggle_history = UserAccountDisableHistorySerializer(many=True, required=False) - class Meta(object): + class Meta: model = User fields = ("username", "email", "date_joined", "is_active", "password_toggle_history") read_only_fields = fields @@ -257,7 +256,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea social_links = SocialLinkSerializer(many=True, required=False) phone_number = PhoneNumberSerializer(required=False) - class Meta(object): + class Meta: model = UserProfile fields = ( "name", "gender", "goals", "year_of_birth", "level_of_education", "country", "state", "social_links", @@ -272,7 +271,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea """ Enforce maximum length for bio. """ if len(new_bio) > BIO_MAX_LENGTH: raise serializers.ValidationError( - u"The about me field must be at most {} characters long.".format(BIO_MAX_LENGTH) + f"The about me field must be at most {BIO_MAX_LENGTH} characters long." ) return new_bio @@ -280,7 +279,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea """ Enforce minimum length for name. """ if len(new_name) < NAME_MIN_LENGTH: raise serializers.ValidationError( - u"The name field must be at least {} character long.".format(NAME_MIN_LENGTH) + f"The name field must be at least {NAME_MIN_LENGTH} character long." ) return new_name @@ -289,7 +288,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea Enforce all languages are unique. """ language_proficiencies = [language for language in value] # lint-amnesty, pylint: disable=unnecessary-comprehension - unique_language_proficiencies = set(language["code"] for language in language_proficiencies) + unique_language_proficiencies = {language["code"] for language in language_proficiencies} if len(language_proficiencies) != len(unique_language_proficiencies): raise serializers.ValidationError("The language_proficiencies field must consist of unique languages.") return value @@ -299,7 +298,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea Enforce only one entry for a particular social platform. """ social_links = [social_link for social_link in value] # lint-amnesty, pylint: disable=unnecessary-comprehension - unique_social_links = set(social_link["platform"] for social_link in social_links) + unique_social_links = {social_link["platform"] for social_link in social_links} if len(social_links) != len(unique_social_links): raise serializers.ValidationError("The social_links field must consist of unique social platforms.") return value @@ -349,7 +348,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea data = {'has_image': user_profile.has_profile_image} urls = get_profile_image_urls_for_user(user, request) data.update({ - '{image_key_prefix}_{size}'.format(image_key_prefix=PROFILE_IMAGE_KEY_PREFIX, size=size_display_name): url + f'{PROFILE_IMAGE_KEY_PREFIX}_{size_display_name}': url for size_display_name, url in urls.items() }) return data @@ -408,8 +407,8 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea # If we have encountered any validation errors, return them to the user. raise errors.AccountValidationError({ 'social_links': { - "developer_message": u"Error when adding new social link: '{}'".format(text_type(err)), - "user_message": text_type(err) + "developer_message": "Error when adding new social link: '{}'".format(str(err)), + "user_message": str(err) } }) @@ -425,7 +424,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea # Update all fields on the user profile that are writeable, # except for "language_proficiencies" and "social_links", which we'll update separately - update_fields = set(self.get_writeable_fields()) - set(["language_proficiencies"]) - set(["social_links"]) + update_fields = set(self.get_writeable_fields()) - {"language_proficiencies"} - {"social_links"} for field_name in update_fields: default = getattr(instance, field_name) field_value = validated_data.get(field_name, default) @@ -452,7 +451,7 @@ class RetirementUserProfileSerializer(serializers.ModelSerializer): """ Serialize a small subset of UserProfile data for use in RetirementStatus APIs """ - class Meta(object): + class Meta: model = UserProfile fields = ('id', 'name') @@ -463,7 +462,7 @@ class RetirementUserSerializer(serializers.ModelSerializer): """ profile = RetirementUserProfileSerializer(read_only=True) - class Meta(object): + class Meta: model = User fields = ('id', 'username', 'email', 'profile') @@ -472,7 +471,7 @@ class RetirementStateSerializer(serializers.ModelSerializer): """ Serialize a small subset of RetirementState data for use in RetirementStatus APIs """ - class Meta(object): + class Meta: model = RetirementState fields = ('id', 'state_name', 'state_execution_order') @@ -485,7 +484,7 @@ class UserRetirementStatusSerializer(serializers.ModelSerializer): current_state = RetirementStateSerializer(read_only=True) last_state = RetirementStateSerializer(read_only=True) - class Meta(object): + class Meta: model = UserRetirementStatus exclude = ['responses', ] @@ -580,6 +579,6 @@ def _visible_fields_from_custom_preferences(user, configuration): preferences = UserPreference.get_all_preferences(user) fields_shared_with_all_users = [ field_name for field_name in configuration.get('custom_shareable_fields') - if preferences.get('{}{}'.format(VISIBILITY_PREFIX, field_name)) == 'all_users' + if preferences.get(f'{VISIBILITY_PREFIX}{field_name}') == 'all_users' ] return set(fields_shared_with_all_users + configuration.get('public_fields')) diff --git a/openedx/core/djangoapps/user_api/accounts/settings_views.py b/openedx/core/djangoapps/user_api/accounts/settings_views.py index 6f9cecdf41..1478313cf6 100644 --- a/openedx/core/djangoapps/user_api/accounts/settings_views.py +++ b/openedx/core/djangoapps/user_api/accounts/settings_views.py @@ -2,9 +2,9 @@ import logging +import urllib from datetime import datetime -import six from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required @@ -64,7 +64,7 @@ def account_settings(request): if duplicate_provider: url = '{url}?{params}'.format( url=url, - params=six.moves.urllib.parse.urlencode({ + params=urllib.parse.urlencode({ 'duplicate_provider': duplicate_provider, }), ) @@ -87,7 +87,7 @@ def account_settings_context(request): """ user = request.user - year_of_birth_options = [(six.text_type(year), six.text_type(year)) for year in UserProfile.VALID_YEARS] + year_of_birth_options = [(str(year), str(year)) for year in UserProfile.VALID_YEARS] try: user_orders = get_user_orders(user) except: # pylint: disable=bare-except @@ -231,19 +231,19 @@ def _get_extended_profile_fields(): 'gender', 'year_of_birth', 'language_proficiencies', 'social_links'] field_labels_map = { - "first_name": _(u"First Name"), - "last_name": _(u"Last Name"), - "city": _(u"City"), - "state": _(u"State/Province/Region"), - "company": _(u"Company"), - "title": _(u"Title"), - "job_title": _(u"Job Title"), - "mailing_address": _(u"Mailing address"), - "goals": _(u"Tell us why you're interested in {platform_name}").format( + "first_name": _("First Name"), + "last_name": _("Last Name"), + "city": _("City"), + "state": _("State/Province/Region"), + "company": _("Company"), + "title": _("Title"), + "job_title": _("Job Title"), + "mailing_address": _("Mailing address"), + "goals": _("Tell us why you're interested in {platform_name}").format( platform_name=configuration_helpers.get_value("PLATFORM_NAME", settings.PLATFORM_NAME) ), - "profession": _(u"Profession"), - "specialty": _(u"Specialty") + "profession": _("Profession"), + "specialty": _("Specialty") } extended_profile_field_names = configuration_helpers.get_value('extended_profile_fields', []) diff --git a/openedx/core/djangoapps/user_api/accounts/utils.py b/openedx/core/djangoapps/user_api/accounts/utils.py index 5be66a0d7e..717e4e4c2c 100644 --- a/openedx/core/djangoapps/user_api/accounts/utils.py +++ b/openedx/core/djangoapps/user_api/accounts/utils.py @@ -4,15 +4,13 @@ Utility methods for the account settings. import re +from urllib.parse import urlparse # pylint: disable=import-error import waffle from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH from completion.models import BlockCompletion from django.conf import settings from django.utils.translation import ugettext as _ -from six import text_type -from six.moves import range # lint-amnesty, pylint: disable=unused-import -from six.moves.urllib.parse import urlparse # pylint: disable=import-error from common.djangoapps.third_party_auth.config.waffle import ENABLE_MULTIPLE_SSO_ACCOUNTS_ASSOCIATION_TO_SAML_USER from openedx.core.djangoapps.site_configuration.models import SiteConfiguration @@ -63,7 +61,7 @@ def format_social_link(platform_name, new_social_link): return None # For security purposes, always build up the url rather than using input from user. - return 'https://www.{}{}'.format(url_stub, username) + return f'https://www.{url_stub}{username}' def _get_username_from_social_link(platform_name, new_social_link): @@ -173,10 +171,10 @@ def retrieve_last_sitewide_block_completed(user): if not (lms_root and item): return - return u"{lms_root}/courses/{course_key}/jump_to/{location}".format( + return "{lms_root}/courses/{course_key}/jump_to/{location}".format( lms_root=lms_root, - course_key=text_type(item.location.course_key), - location=text_type(item.location), + course_key=str(item.location.course_key), + location=str(item.location), ) diff --git a/openedx/core/djangoapps/user_api/accounts/views.py b/openedx/core/djangoapps/user_api/accounts/views.py index 54ad3cccc2..45963bfc5d 100644 --- a/openedx/core/djangoapps/user_api/accounts/views.py +++ b/openedx/core/djangoapps/user_api/accounts/views.py @@ -34,7 +34,6 @@ from rest_framework.response import Response from rest_framework.serializers import ValidationError from rest_framework.views import APIView from rest_framework.viewsets import ViewSet -from six import iteritems, text_type from social_django.models import UserSocialAuth from wiki.models import ArticleRevision from wiki.models.pluginbase import RevisionPluginRevision @@ -115,7 +114,7 @@ def request_requires_username(function): if not username: return Response( status=status.HTTP_404_NOT_FOUND, - data={'message': text_type('The user was not specified.')} + data={'message': 'The user was not specified.'} ) return function(self, request) return wrapper @@ -469,16 +468,16 @@ class DeactivateLogoutView(APIView): logout(request) return Response(status=status.HTTP_204_NO_CONTENT) except KeyError: - log.exception('Username not specified {}'.format(request.user)) - return Response(u'Username not specified.', status=status.HTTP_404_NOT_FOUND) + log.exception(f'Username not specified {request.user}') + return Response('Username not specified.', status=status.HTTP_404_NOT_FOUND) except user_model.DoesNotExist: - log.exception('The user "{}" does not exist.'.format(request.user.username)) + log.exception(f'The user "{request.user.username}" does not exist.') return Response( - u'The user "{}" does not exist.'.format(request.user.username), status=status.HTTP_404_NOT_FOUND + f'The user "{request.user.username}" does not exist.', status=status.HTTP_404_NOT_FOUND ) except Exception as exc: # pylint: disable=broad-except - log.exception('500 error deactivating account {}'.format(exc)) - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + log.exception(f'500 error deactivating account {exc}') + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) def _verify_user_password(self, request): """ @@ -499,11 +498,11 @@ class DeactivateLogoutView(APIView): self._handle_failed_authentication(request.user) except AuthFailedError as err: log.exception( - "The user password to deactivate was incorrect. {}".format(request.user.username) + f"The user password to deactivate was incorrect. {request.user.username}" ) - return Response(text_type(err), status=status.HTTP_403_FORBIDDEN) + return Response(str(err), status=status.HTTP_403_FORBIDDEN) except Exception as err: # pylint: disable=broad-except - return Response(u"Could not verify user password: {}".format(err), status=status.HTTP_400_BAD_REQUEST) + return Response(f"Could not verify user password: {err}", status=status.HTTP_400_BAD_REQUEST) def _check_excessive_login_attempts(self, user): """ @@ -713,9 +712,9 @@ class AccountRetirementPartnerReportView(ViewSet): # to disambiguate them in Python, which will respect case in the comparison. if len(usernames) != len(retirement_statuses_clean): return Response( - u'{} original_usernames given, {} found!\n' - u'Given usernames:\n{}\n' - u'Found UserRetirementReportingStatuses:\n{}'.format( + '{} original_usernames given, {} found!\n' + 'Given usernames:\n{}\n' + 'Found UserRetirementReportingStatuses:\n{}'.format( len(usernames), len(retirement_statuses_clean), usernames, @@ -758,7 +757,7 @@ class AccountRetirementStatusView(ViewSet): state_objs = RetirementState.objects.filter(state_name__in=states) if state_objs.count() != len(states): found = [s.state_name for s in state_objs] - raise RetirementStateError(u'Unknown state. Requested: {} Found: {}'.format(states, found)) + raise RetirementStateError(f'Unknown state. Requested: {states} Found: {found}') earliest_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=cool_off_days) @@ -775,10 +774,10 @@ class AccountRetirementStatusView(ViewSet): except ValueError: return Response('Invalid cool_off_days, should be integer.', status=status.HTTP_400_BAD_REQUEST) except KeyError as exc: - return Response(u'Missing required parameter: {}'.format(text_type(exc)), + return Response('Missing required parameter: {}'.format(str(exc)), status=status.HTTP_400_BAD_REQUEST) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) def retirements_by_status_and_date(self, request): """ @@ -815,14 +814,14 @@ class AccountRetirementStatusView(ViewSet): return Response(serializer.data) # This should only occur on the datetime conversion of the start / end dates. except ValueError as exc: - return Response(u'Invalid start or end date: {}'.format(text_type(exc)), status=status.HTTP_400_BAD_REQUEST) + return Response('Invalid start or end date: {}'.format(str(exc)), status=status.HTTP_400_BAD_REQUEST) except KeyError as exc: - return Response(u'Missing required parameter: {}'.format(text_type(exc)), + return Response('Missing required parameter: {}'.format(str(exc)), status=status.HTTP_400_BAD_REQUEST) except RetirementState.DoesNotExist: return Response('Unknown retirement state.', status=status.HTTP_400_BAD_REQUEST) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, username): # pylint: disable=unused-argument """ @@ -886,9 +885,9 @@ class AccountRetirementStatusView(ViewSet): except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) def cleanup(self, request): """ @@ -921,9 +920,9 @@ class AccountRetirementStatusView(ViewSet): retirements.delete() return Response(status=status.HTTP_204_NO_CONTENT) except (RetirementStateError, UserRetirementStatus.DoesNotExist, TypeError) as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) class LMSAccountRetirementView(ViewSet): @@ -975,9 +974,9 @@ class LMSAccountRetirementView(ViewSet): except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT) @@ -1045,9 +1044,9 @@ class AccountRetirementView(ViewSet): except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: - return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) + return Response(str(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except - return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(str(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT) @@ -1057,7 +1056,7 @@ class AccountRetirementView(ViewSet): For the given user, sets all of the user's profile fields to some retired value. This also deletes all ``SocialLink`` objects associated with this user's profile. """ - for model_field, value_to_assign in iteritems(USER_PROFILE_PII): + for model_field, value_to_assign in USER_PROFILE_PII.items(): setattr(user.profile, model_field, value_to_assign) user.profile.save() @@ -1260,7 +1259,7 @@ class UsernameReplacementView(APIView): ) except Exception as exc: # pylint: disable=broad-except log.exception( - u"Unable to change username from %s to %s. Failed on table %s because %s", + "Unable to change username from %s to %s. Failed on table %s because %s", current_username, new_username, model.__class__.__name__, # Retrieves the model name that it failed on @@ -1269,14 +1268,14 @@ class UsernameReplacementView(APIView): return False if num_rows_changed == 0: log.info( - u"Unable to change username from %s to %s because %s doesn't exist.", + "Unable to change username from %s to %s because %s doesn't exist.", current_username, new_username, current_username, ) else: log.info( - u"Successfully changed username from %s to %s.", + "Successfully changed username from %s to %s.", current_username, new_username, ) diff --git a/openedx/core/djangoapps/user_api/admin.py b/openedx/core/djangoapps/user_api/admin.py index d618a3a780..9de740a0e3 100644 --- a/openedx/core/djangoapps/user_api/admin.py +++ b/openedx/core/djangoapps/user_api/admin.py @@ -26,7 +26,7 @@ class RetirementStateAdmin(admin.ModelAdmin): list_filter = ('is_dead_end_state', 'required',) search_fields = ('state_name',) - class Meta(object): + class Meta: model = RetirementState @@ -87,7 +87,7 @@ class UserRetirementStatusAdmin(admin.ModelAdmin): """ Adds our custom URL to the admin """ - urls = super(UserRetirementStatusAdmin, self).get_urls() # lint-amnesty, pylint: disable=super-with-arguments + urls = super().get_urls() custom_urls = [ url( r'^(?P.+)/cancel_retirement/$', @@ -105,7 +105,7 @@ class UserRetirementStatusAdmin(admin.ModelAdmin): try: if obj.current_state.state_name == 'PENDING': return format_html( - u'{} ', + '{} ', reverse('admin:cancel-retirement', args=[obj.pk]), _('Cancel') ) @@ -121,7 +121,7 @@ class UserRetirementStatusAdmin(admin.ModelAdmin): Removes the default bulk delete option provided by Django, it doesn't do what we need for this model. """ - actions = super(UserRetirementStatusAdmin, self).get_actions(request) # lint-amnesty, pylint: disable=super-with-arguments + actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions @@ -138,7 +138,7 @@ class UserRetirementStatusAdmin(admin.ModelAdmin): """ return False - class Meta(object): + class Meta: model = UserRetirementStatus @@ -150,7 +150,7 @@ class UserRetirementRequestAdmin(admin.ModelAdmin): list_display = ('user', 'created') raw_id_fields = ('user',) - class Meta(object): + class Meta: model = UserRetirementRequest @@ -172,7 +172,7 @@ class UserRetirementPartnerReportingStatusAdmin(admin.ModelAdmin): 'reset_state', # See reset_state() below. ] - class Meta(object): + class Meta: model = UserRetirementPartnerReportingStatus def user_id(self, obj): @@ -192,7 +192,7 @@ class UserRetirementPartnerReportingStatusAdmin(admin.ModelAdmin): if rows_updated == 1: message_bit = "one user was" else: - message_bit = u"%s users were" % rows_updated - self.message_user(request, u"%s successfully reset." % message_bit) + message_bit = "%s users were" % rows_updated + self.message_user(request, "%s successfully reset." % message_bit) reset_state.short_description = 'Reset is_being_processed to False' diff --git a/openedx/core/djangoapps/user_api/config/waffle.py b/openedx/core/djangoapps/user_api/config/waffle.py index 34aed2b38d..792d722e1e 100644 --- a/openedx/core/djangoapps/user_api/config/waffle.py +++ b/openedx/core/djangoapps/user_api/config/waffle.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from edx_toggles.toggles import WaffleSwitch -SYSTEM_MAINTENANCE_MSG = _(u'System maintenance in progress. Please try again later.') +SYSTEM_MAINTENANCE_MSG = _('System maintenance in progress. Please try again later.') # .. toggle_name: user_api.enable_multiple_user_enterprises_feature # .. toggle_implementation: WaffleSwitch diff --git a/openedx/core/djangoapps/user_api/course_tag/api.py b/openedx/core/djangoapps/user_api/course_tag/api.py index d17d32a676..36c4ee4f87 100644 --- a/openedx/core/djangoapps/user_api/course_tag/api.py +++ b/openedx/core/djangoapps/user_api/course_tag/api.py @@ -20,8 +20,8 @@ from ..models import UserCourseTag COURSE_SCOPE = 'course' -class BulkCourseTags(object): # lint-amnesty, pylint: disable=missing-class-docstring - CACHE_NAMESPACE = u'user_api.course_tag.api' +class BulkCourseTags: # lint-amnesty, pylint: disable=missing-class-docstring + CACHE_NAMESPACE = 'user_api.course_tag.api' @classmethod def prefetch(cls, course_id, users): @@ -53,7 +53,7 @@ class BulkCourseTags(object): # lint-amnesty, pylint: disable=missing-class-doc @classmethod def _cache_key(cls, course_id): - return u'course_tag.{}'.format(course_id) + return f'course_tag.{course_id}' def get_course_tag(user, course_id, key): diff --git a/openedx/core/djangoapps/user_api/errors.py b/openedx/core/djangoapps/user_api/errors.py index 1a733f2e63..2bde3b182a 100644 --- a/openedx/core/djangoapps/user_api/errors.py +++ b/openedx/core/djangoapps/user_api/errors.py @@ -108,7 +108,7 @@ class PreferenceValidationError(PreferenceRequestError): """ def __init__(self, preference_errors): self.preference_errors = preference_errors - super(PreferenceValidationError, self).__init__(preference_errors) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(preference_errors) class PreferenceUpdateError(PreferenceRequestError): @@ -119,7 +119,7 @@ class PreferenceUpdateError(PreferenceRequestError): def __init__(self, developer_message, user_message=None): self.developer_message = developer_message self.user_message = user_message - super(PreferenceUpdateError, self).__init__(developer_message) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(developer_message) class CountryCodeError(ValueError): diff --git a/openedx/core/djangoapps/user_api/helpers.py b/openedx/core/djangoapps/user_api/helpers.py index 487f64891e..8f1c51ca5d 100644 --- a/openedx/core/djangoapps/user_api/helpers.py +++ b/openedx/core/djangoapps/user_api/helpers.py @@ -10,7 +10,6 @@ import traceback from collections import defaultdict from functools import wraps -import six from django import forms from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder @@ -57,9 +56,9 @@ def intercept_errors(api_error, ignore_errors=None): for ignored in ignore_errors or []: if isinstance(ex, ignored): msg = ( - u"A handled error occurred when calling '{func_name}' " - u"with arguments '{args}' and keyword arguments '{kwargs}': " - u"{exception}" + "A handled error occurred when calling '{func_name}' " + "with arguments '{args}' and keyword arguments '{kwargs}': " + "{exception}" ).format( func_name=func.__name__, args=args, @@ -73,9 +72,9 @@ def intercept_errors(api_error, ignore_errors=None): # Otherwise, log the error and raise the API-specific error msg = ( - u"An unexpected error occurred when calling '{func_name}' " - u"with arguments '{args}' and keyword arguments '{kwargs}' from {caller}: " - u"{exception}" + "An unexpected error occurred when calling '{func_name}' " + "with arguments '{args}' and keyword arguments '{kwargs}' from {caller}: " + "{exception}" ).format( func_name=func.__name__, args=args, @@ -93,7 +92,7 @@ class InvalidFieldError(Exception): """The provided field definition is not valid. """ -class FormDescription(object): +class FormDescription: """Generate a JSON representation of a form. """ ALLOWED_TYPES = ["text", "email", "select", "textarea", "checkbox", "plaintext", "password", "hidden"] @@ -135,10 +134,10 @@ class FormDescription(object): self._field_overrides = defaultdict(dict) def add_field( - self, name, label=u"", field_type=u"text", default=u"", - placeholder=u"", instructions=u"", required=True, restrictions=None, + self, name, label="", field_type="text", default="", + placeholder="", instructions="", required=True, restrictions=None, options=None, include_default_option=False, error_messages=None, - supplementalLink=u"", supplementalText=u"" + supplementalLink="", supplementalText="" ): """Add a field to the form description. @@ -190,7 +189,7 @@ class FormDescription(object): """ if field_type not in self.ALLOWED_TYPES: - msg = u"Field type '{field_type}' is not a valid type. Allowed types are: {allowed}.".format( + msg = "Field type '{field_type}' is not a valid type. Allowed types are: {allowed}.".format( field_type=field_type, allowed=", ".join(self.ALLOWED_TYPES) ) @@ -240,14 +239,11 @@ class FormDescription(object): if restrictions is not None: allowed_restrictions = self.ALLOWED_RESTRICTIONS.get(field_type, []) - for key, val in six.iteritems(restrictions): + for key, val in restrictions.items(): if key in allowed_restrictions: field_dict["restrictions"][key] = val else: - msg = u"Restriction '{restriction}' is not allowed for field type '{field_type}'".format( - restriction=key, - field_type=field_type - ) + msg = f"Restriction '{key}' is not allowed for field type '{field_type}'" raise InvalidFieldError(msg) if error_messages is not None: @@ -337,7 +333,7 @@ class FormDescription(object): self._field_overrides[field_name].update({ property_name: property_value - for property_name, property_value in six.iteritems(kwargs) + for property_name, property_value in kwargs.items() if property_name in self.OVERRIDE_FIELD_PROPERTIES }) @@ -353,7 +349,7 @@ class LocalizedJSONEncoder(DjangoJSONEncoder): """ if isinstance(obj, Promise): return force_text(obj) - super(LocalizedJSONEncoder, self).default(obj) # lint-amnesty, pylint: disable=super-with-arguments + super().default(obj) def serializer_is_dirty(preference_serializer): diff --git a/openedx/core/djangoapps/user_api/legacy_urls.py b/openedx/core/djangoapps/user_api/legacy_urls.py index 3068e7d2d3..04318cfc46 100644 --- a/openedx/core/djangoapps/user_api/legacy_urls.py +++ b/openedx/core/djangoapps/user_api/legacy_urls.py @@ -19,7 +19,7 @@ urlpatterns = [ url(r'^account/settings$', account_settings, name='account_settings'), url(r'^user_api/v1/', include(USER_API_ROUTER.urls)), url( - r'^user_api/v1/preferences/(?P{})/users/$'.format(UserPreference.KEY_REGEX), + fr'^user_api/v1/preferences/(?P{UserPreference.KEY_REGEX})/users/$', user_api_views.PreferenceUsersListView.as_view() ), url( diff --git a/openedx/core/djangoapps/user_api/message_types.py b/openedx/core/djangoapps/user_api/message_types.py index b7fc85728e..d7314557e3 100644 --- a/openedx/core/djangoapps/user_api/message_types.py +++ b/openedx/core/djangoapps/user_api/message_types.py @@ -14,7 +14,7 @@ class DeletionNotificationMessage(BaseMessageType): Message to notify learners that their account is queued for deletion. """ def __init__(self, *args, **kwargs): - super(DeletionNotificationMessage, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.options['transactional'] = True # pylint: disable=unsupported-assignment-operation self.options['from_address'] = helpers.get_value( # pylint: disable=unsupported-assignment-operation diff --git a/openedx/core/djangoapps/user_api/migrations/0001_initial.py b/openedx/core/djangoapps/user_api/migrations/0001_initial.py index 0f7c02ccab..b6964d35e9 100644 --- a/openedx/core/djangoapps/user_api/migrations/0001_initial.py +++ b/openedx/core/djangoapps/user_api/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - import django.core.validators import django.utils.timezone import model_utils.fields @@ -42,21 +39,21 @@ class Migration(migrations.Migration): name='UserPreference', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('key', models.CharField(db_index=True, max_length=255, validators=[django.core.validators.RegexValidator(u'[-_a-zA-Z0-9]+')])), + ('key', models.CharField(db_index=True, max_length=255, validators=[django.core.validators.RegexValidator('[-_a-zA-Z0-9]+')])), ('value', models.TextField()), ('user', models.ForeignKey(related_name='preferences', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), migrations.AlterUniqueTogether( name='userpreference', - unique_together=set([('user', 'key')]), + unique_together={('user', 'key')}, ), migrations.AlterUniqueTogether( name='userorgtag', - unique_together=set([('user', 'org', 'key')]), + unique_together={('user', 'org', 'key')}, ), migrations.AlterUniqueTogether( name='usercoursetag', - unique_together=set([('user', 'course_id', 'key')]), + unique_together={('user', 'course_id', 'key')}, ), ] diff --git a/openedx/core/djangoapps/user_api/migrations/0002_retirementstate_userretirementstatus.py b/openedx/core/djangoapps/user_api/migrations/0002_retirementstate_userretirementstatus.py index 272a9c4b7b..5d9ad93f2c 100644 --- a/openedx/core/djangoapps/user_api/migrations/0002_retirementstate_userretirementstatus.py +++ b/openedx/core/djangoapps/user_api/migrations/0002_retirementstate_userretirementstatus.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.12 on 2018-04-19 17:55 diff --git a/openedx/core/djangoapps/user_api/migrations/0003_userretirementrequest.py b/openedx/core/djangoapps/user_api/migrations/0003_userretirementrequest.py index 4a885470f0..4e4b93c8d3 100644 --- a/openedx/core/djangoapps/user_api/migrations/0003_userretirementrequest.py +++ b/openedx/core/djangoapps/user_api/migrations/0003_userretirementrequest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.13 on 2018-05-14 20:37 diff --git a/openedx/core/djangoapps/user_api/migrations/0004_userretirementpartnerreportingstatus.py b/openedx/core/djangoapps/user_api/migrations/0004_userretirementpartnerreportingstatus.py index 3fe78479be..5dc9bc20e2 100644 --- a/openedx/core/djangoapps/user_api/migrations/0004_userretirementpartnerreportingstatus.py +++ b/openedx/core/djangoapps/user_api/migrations/0004_userretirementpartnerreportingstatus.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.13 on 2018-06-13 20:54 diff --git a/openedx/core/djangoapps/user_api/models.py b/openedx/core/djangoapps/user_api/models.py index 94425638b9..c1beac1f5f 100644 --- a/openedx/core/djangoapps/user_api/models.py +++ b/openedx/core/djangoapps/user_api/models.py @@ -45,12 +45,12 @@ class UserPreference(models.Model): .. no_pii: Stores arbitrary key/value pairs, currently none are PII. If that changes, update this annotation. """ - KEY_REGEX = u"[-_a-zA-Z0-9]+" + KEY_REGEX = "[-_a-zA-Z0-9]+" user = models.ForeignKey(User, db_index=True, related_name="preferences", on_delete=models.CASCADE) key = models.CharField(max_length=255, db_index=True, validators=[RegexValidator(KEY_REGEX)]) value = models.TextField() - class Meta(object): + class Meta: unique_together = ("user", "key") @staticmethod @@ -61,7 +61,7 @@ class UserPreference(models.Model): Returns: Set of (preference type, value) pairs for each of the user's preferences """ - return dict([(pref.key, pref.value) for pref in user.preferences.all()]) # lint-amnesty, pylint: disable=consider-using-dict-comprehension + return {pref.key: pref.value for pref in user.preferences.all()} @classmethod def get_value(cls, user, preference_key, default=None): @@ -158,7 +158,7 @@ class UserCourseTag(models.Model): course_id = CourseKeyField(max_length=255, db_index=True) value = models.TextField() - class Meta(object): + class Meta: unique_together = ("user", "course_id", "key") @@ -177,7 +177,7 @@ class UserOrgTag(TimeStampedModel, DeletableByUserValue): org = models.CharField(max_length=255, db_index=True) value = models.TextField() - class Meta(object): + class Meta: unique_together = ("user", "org", "key") @@ -195,9 +195,9 @@ class RetirementState(models.Model): required = models.BooleanField(default=False) def __str__(self): - return '{} (step {})'.format(self.state_name, self.state_execution_order) + return f'{self.state_name} (step {self.state_execution_order})' - class Meta(object): + class Meta: ordering = ('state_execution_order',) @classmethod @@ -232,12 +232,12 @@ class UserRetirementPartnerReportingStatus(TimeStampedModel): original_name = models.CharField(max_length=255, blank=True, db_index=True) is_being_processed = models.BooleanField(default=False) - class Meta(object): + class Meta: verbose_name = 'User Retirement Reporting Status' verbose_name_plural = 'User Retirement Reporting Statuses' def __str__(self): - return u'UserRetirementPartnerReportingStatus: {} is being processed: {}'.format( + return 'UserRetirementPartnerReportingStatus: {} is being processed: {}'.format( self.user, self.is_being_processed ) @@ -254,7 +254,7 @@ class UserRetirementRequest(TimeStampedModel): """ user = models.OneToOneField(User, on_delete=models.CASCADE) - class Meta(object): + class Meta: verbose_name = 'User Retirement Request' verbose_name_plural = 'User Retirement Requests' @@ -264,7 +264,7 @@ class UserRetirementRequest(TimeStampedModel): Creates a UserRetirementRequest for the specified user. """ if cls.has_user_requested_retirement(user): - raise RetirementStateError(u'User {} already has a retirement request row!'.format(user)) + raise RetirementStateError(f'User {user} already has a retirement request row!') return cls.objects.create(user=user) @classmethod @@ -275,10 +275,9 @@ class UserRetirementRequest(TimeStampedModel): return cls.objects.filter(user=user).exists() def __str__(self): - return u'User: {} Requested: {}'.format(self.user.id, self.created) + return f'User: {self.user.id} Requested: {self.created}' -@python_2_unicode_compatible class UserRetirementStatus(TimeStampedModel): """ Tracks the progress of a user's retirement request @@ -297,7 +296,7 @@ class UserRetirementStatus(TimeStampedModel): last_state = models.ForeignKey(RetirementState, blank=True, related_name='last_state', on_delete=models.CASCADE) responses = models.TextField() - class Meta(object): + class Meta: verbose_name = 'User Retirement Status' verbose_name_plural = 'User Retirement Statuses' @@ -308,16 +307,14 @@ class UserRetirementStatus(TimeStampedModel): dead_end_states = list(RetirementState.get_dead_end_state_names_list()) states = list(RetirementState.get_state_names_list()) if self.current_state in dead_end_states: - raise RetirementStateError(u'RetirementStatus: Unable to move user from {}'.format(self.current_state)) + raise RetirementStateError(f'RetirementStatus: Unable to move user from {self.current_state}') try: new_state_index = states.index(new_state) if new_state_index <= states.index(self.current_state.state_name): raise ValueError() except ValueError: - err = u'{} does not exist or is an eariler state than current state {}'.format( - new_state, self.current_state - ) + err = f'{new_state} does not exist or is an eariler state than current state {self.current_state}' raise RetirementStateError(err) # lint-amnesty, pylint: disable=raise-missing-from def _validate_update_data(self, data): @@ -330,13 +327,11 @@ class UserRetirementStatus(TimeStampedModel): for required_key in required_keys: if required_key not in data: - raise RetirementStateError(u'RetirementStatus: Required key {} missing from update'.format( - required_key - )) + raise RetirementStateError(f'RetirementStatus: Required key {required_key} missing from update') for key in data: if key not in known_keys: - raise RetirementStateError(u'RetirementStatus: Unknown key {} in update'.format(key)) + raise RetirementStateError(f'RetirementStatus: Unknown key {key} in update') @classmethod def create_retirement(cls, user): @@ -350,7 +345,7 @@ class UserRetirementStatus(TimeStampedModel): raise RetirementStateError('Default state does not exist! Populate retirement states to retire users.') # lint-amnesty, pylint: disable=raise-missing-from if cls.objects.filter(user=user).exists(): - raise RetirementStateError(u'User {} already has a retirement status row!'.format(user)) + raise RetirementStateError(f'User {user} already has a retirement status row!') retired_username = get_retired_username_by_username(user.username) retired_email = get_retired_email_by_email(user.email) @@ -366,7 +361,7 @@ class UserRetirementStatus(TimeStampedModel): retired_email=retired_email, current_state=pending, last_state=pending, - responses=u'Created in state {} by create_retirement'.format(pending) + responses=f'Created in state {pending} by create_retirement' ) def update_state(self, update): @@ -383,7 +378,7 @@ class UserRetirementStatus(TimeStampedModel): old_state = self.current_state self.current_state = RetirementState.objects.get(state_name=update['new_state']) self.last_state = old_state - self.responses += u"\n Moved from {} to {}:\n{}\n".format(old_state, self.current_state, update['response']) + self.responses += "\n Moved from {} to {}:\n{}\n".format(old_state, self.current_state, update['response']) self.save() @classmethod @@ -411,19 +406,19 @@ class UserRetirementStatus(TimeStampedModel): break if retirement is None: - raise UserRetirementStatus.DoesNotExist(u'{} does not have an exact match in UserRetirementStatus. ' - u'{} similar rows found.'.format(username, len(retirements))) + raise UserRetirementStatus.DoesNotExist('{} does not have an exact match in UserRetirementStatus. ' + '{} similar rows found.'.format(username, len(retirements))) state = retirement.current_state if state.required or state.state_name.endswith('_COMPLETE'): - raise RetirementStateError(u'{} is in {}, not a valid state to perform retirement ' - u'actions on.'.format(retirement, state.state_name)) + raise RetirementStateError('{} is in {}, not a valid state to perform retirement ' + 'actions on.'.format(retirement, state.state_name)) return retirement def __str__(self): - return u'User: {} State: {} Last Updated: {}'.format(self.user.id, self.current_state, self.modified) + return f'User: {self.user.id} State: {self.current_state} Last Updated: {self.modified}' @receiver(models.signals.post_delete, sender=UserRetirementStatus) diff --git a/openedx/core/djangoapps/user_api/partition_schemes.py b/openedx/core/djangoapps/user_api/partition_schemes.py index eeb55faa17..d69cbbf791 100644 --- a/openedx/core/djangoapps/user_api/partition_schemes.py +++ b/openedx/core/djangoapps/user_api/partition_schemes.py @@ -14,7 +14,7 @@ from xmodule.partitions.partitions import NoSuchUserPartitionGroupError, UserPar log = logging.getLogger(__name__) -class NotImplementedPartitionScheme(object): +class NotImplementedPartitionScheme: """ This "scheme" allows previously-defined schemes to be purged, while giving existing course data definitions a safe entry point to load. @@ -30,7 +30,7 @@ class NotImplementedPartitionScheme(object): return None -class ReturnGroup1PartitionScheme(object): +class ReturnGroup1PartitionScheme: """ This scheme is needed to allow verification partitions to be killed, see EDUCATOR-199 """ @@ -43,7 +43,7 @@ class ReturnGroup1PartitionScheme(object): return user_partition.get_group(1) -class RandomUserPartitionScheme(object): +class RandomUserPartitionScheme: """ This scheme randomly assigns users into the partition's groups. """ @@ -66,7 +66,7 @@ class RandomUserPartitionScheme(object): except NoSuchUserPartitionGroupError: # jsa: we can turn off warnings here if this is an expected case. log.warning( - u"group not found in RandomUserPartitionScheme: %r", + "group not found in RandomUserPartitionScheme: %r", { "requested_partition_id": user_partition.id, "requested_group_id": group_id, @@ -74,7 +74,7 @@ class RandomUserPartitionScheme(object): exc_info=True ) except ValueError: - log.error(u"Bad group_id %r for user: %r", group_id, user) + log.error("Bad group_id %r for user: %r", group_id, user) if group is None and assign and not course_tag_api.BulkCourseTags.is_prefetched(course_key): if not user_partition.groups: @@ -115,4 +115,4 @@ class RandomUserPartitionScheme(object): """ Returns the key to use to look up and save the user's group for a given user partition. """ - return 'xblock.partition_service.partition_{0}'.format(user_partition.id) + return f'xblock.partition_service.partition_{user_partition.id}' diff --git a/openedx/core/djangoapps/user_api/preferences/api.py b/openedx/core/djangoapps/user_api/preferences/api.py index 72987591b1..c68800c1e0 100644 --- a/openedx/core/djangoapps/user_api/preferences/api.py +++ b/openedx/core/djangoapps/user_api/preferences/api.py @@ -5,7 +5,6 @@ API for managing user preferences. import logging -import six from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.db import IntegrityError @@ -134,7 +133,7 @@ def update_user_preferences(requesting_user, update, user=None): PreferenceUpdateError: the operation failed when performing the update. UserAPIInternalError: the operation failed due to an unexpected error. """ - if not user or isinstance(user, six.string_types): + if not user or isinstance(user, str): user = _get_authorized_user(requesting_user, user) else: _check_authorized(requesting_user, user.username) @@ -145,7 +144,7 @@ def update_user_preferences(requesting_user, update, user=None): for preference_key in update.keys(): preference_value = update[preference_key] if preference_value is not None: - preference_value = six.text_type(preference_value) + preference_value = str(preference_value) try: serializer = create_user_preference_serializer(user, preference_key, preference_value) validate_user_preference_serializer(serializer, preference_key, preference_value) @@ -162,7 +161,7 @@ def update_user_preferences(requesting_user, update, user=None): for preference_key in update.keys(): preference_value = update[preference_key] if preference_value is not None: - preference_value = six.text_type(preference_value) + preference_value = str(preference_value) try: serializer = serializers[preference_key] @@ -201,7 +200,7 @@ def set_user_preference(requesting_user, preference_key, preference_value, usern """ existing_user = _get_authorized_user(requesting_user, username) if preference_value is not None: - preference_value = six.text_type(preference_value) + preference_value = str(preference_value) serializer = create_user_preference_serializer(existing_user, preference_key, preference_value) validate_user_preference_serializer(serializer, preference_key, preference_value) @@ -248,10 +247,10 @@ def delete_user_preference(requesting_user, preference_key, username=None): user_preference.delete() except Exception as error: raise PreferenceUpdateError( # lint-amnesty, pylint: disable=raise-missing-from - developer_message=u"Delete failed for user preference '{preference_key}': {error}".format( + developer_message="Delete failed for user preference '{preference_key}': {error}".format( preference_key=preference_key, error=error ), - user_message=_(u"Delete failed for user preference '{preference_key}'.").format( + user_message=_("Delete failed for user preference '{preference_key}'.").format( preference_key=preference_key ), ) @@ -300,7 +299,7 @@ def update_email_opt_in(user, org, opt_in): _track_update_email_opt_in(user.id, org, opt_in) except IntegrityError as err: log.warning( - u"Could not update organization wide preference due to IntegrityError: {}".format(six.text_type(err)) + "Could not update organization wide preference due to IntegrityError: {}".format(str(err)) ) @@ -399,8 +398,8 @@ def validate_user_preference_serializer(serializer, preference_key, preference_v Raises: PreferenceValidationError: the supplied key and/or value for a user preference are invalid. """ - if preference_value is None or six.text_type(preference_value).strip() == '': - format_string = ugettext_noop(u"Preference '{preference_key}' cannot be set to an empty value.") + if preference_value is None or str(preference_value).strip() == '': + format_string = ugettext_noop("Preference '{preference_key}' cannot be set to an empty value.") raise PreferenceValidationError({ preference_key: { "developer_message": format_string.format(preference_key=preference_key), @@ -412,16 +411,16 @@ def validate_user_preference_serializer(serializer, preference_key, preference_v # DRF error messages are of type ErrorDetail and serialize out as such. We want to coerce those # messages into the strings only. for key in errors: - errors[key] = [six.text_type(el) for el in errors[key]] - developer_message = u"Value '{preference_value}' not valid for preference '{preference_key}': {error}".format( + errors[key] = [str(el) for el in errors[key]] + developer_message = "Value '{preference_value}' not valid for preference '{preference_key}': {error}".format( preference_key=preference_key, preference_value=preference_value, error=errors ) if "key" in serializer.errors: - user_message = _(u"Invalid user preference key '{preference_key}'.").format( + user_message = _("Invalid user preference key '{preference_key}'.").format( preference_key=preference_key ) else: - user_message = _(u"Value '{preference_value}' is not valid for user preference '{preference_key}'.").format( + user_message = _("Value '{preference_value}' is not valid for user preference '{preference_key}'.").format( preference_key=preference_key, preference_value=preference_value ) raise PreferenceValidationError({ @@ -431,8 +430,8 @@ def validate_user_preference_serializer(serializer, preference_key, preference_v } }) if preference_key == "time_zone" and preference_value not in common_timezones_set: - developer_message = ugettext_noop(u"Value '{preference_value}' not valid for preference '{preference_key}': Not in timezone set.") # pylint: disable=line-too-long - user_message = ugettext_noop(u"Value '{preference_value}' is not a valid time zone selection.") + developer_message = ugettext_noop("Value '{preference_value}' not valid for preference '{preference_key}': Not in timezone set.") # pylint: disable=line-too-long + user_message = ugettext_noop("Value '{preference_value}' is not a valid time zone selection.") raise PreferenceValidationError({ preference_key: { "developer_message": developer_message.format( @@ -446,10 +445,10 @@ def validate_user_preference_serializer(serializer, preference_key, preference_v def _create_preference_update_error(preference_key, preference_value, error): """ Creates a PreferenceUpdateError with developer_message and user_message. """ return PreferenceUpdateError( - developer_message=u"Save failed for user preference '{key}' with value '{value}': {error}".format( + developer_message="Save failed for user preference '{key}' with value '{value}': {error}".format( key=preference_key, value=preference_value, error=error ), - user_message=_(u"Save failed for user preference '{key}' with value '{value}'.").format( + user_message=_("Save failed for user preference '{key}' with value '{value}'.").format( key=preference_key, value=preference_value ), ) diff --git a/openedx/core/djangoapps/user_api/serializers.py b/openedx/core/djangoapps/user_api/serializers.py index 28a3301258..509a80d658 100644 --- a/openedx/core/djangoapps/user_api/serializers.py +++ b/openedx/core/djangoapps/user_api/serializers.py @@ -34,7 +34,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): """ return UserPreference.get_all_preferences(user) - class Meta(object): + class Meta: model = User # This list is the minimal set required by the notification service fields = ("id", "url", "email", "name", "username", "preferences") @@ -49,7 +49,7 @@ class UserPreferenceSerializer(serializers.HyperlinkedModelSerializer): """ user = UserSerializer() - class Meta(object): + class Meta: model = UserPreference depth = 1 fields = ('user', 'key', 'value', 'url') @@ -61,13 +61,13 @@ class RawUserPreferenceSerializer(serializers.ModelSerializer): """ user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all()) - class Meta(object): + class Meta: model = UserPreference depth = 1 fields = ('user', 'key', 'value', 'url') -class ReadOnlyFieldsSerializerMixin(object): +class ReadOnlyFieldsSerializerMixin: """ Mixin for use with Serializers that provides a method `get_read_only_fields`, which returns a tuple of all read-only diff --git a/openedx/core/djangoapps/user_api/tests/factories.py b/openedx/core/djangoapps/user_api/tests/factories.py index ff8a18fa13..c3b11367bd 100644 --- a/openedx/core/djangoapps/user_api/tests/factories.py +++ b/openedx/core/djangoapps/user_api/tests/factories.py @@ -13,7 +13,7 @@ from ..models import UserCourseTag, UserOrgTag, UserPreference # Factories are self documenting # pylint: disable=missing-docstring class UserPreferenceFactory(DjangoModelFactory): - class Meta(object): + class Meta: model = UserPreference user = None @@ -22,7 +22,7 @@ class UserPreferenceFactory(DjangoModelFactory): class UserCourseTagFactory(DjangoModelFactory): - class Meta(object): + class Meta: model = UserCourseTag user = SubFactory(UserFactory) @@ -33,7 +33,7 @@ class UserCourseTagFactory(DjangoModelFactory): class UserOrgTagFactory(DjangoModelFactory): """ Simple factory class for generating UserOrgTags """ - class Meta(object): + class Meta: model = UserOrgTag user = SubFactory(UserFactory) diff --git a/openedx/core/djangoapps/user_api/tests/test_constants.py b/openedx/core/djangoapps/user_api/tests/test_constants.py index 012591627d..0d9ee560f5 100644 --- a/openedx/core/djangoapps/user_api/tests/test_constants.py +++ b/openedx/core/djangoapps/user_api/tests/test_constants.py @@ -1,254 +1,253 @@ -# -*- coding: utf-8 -*- """Constants used in the test suite. """ SORTED_COUNTRIES = [ - (u"AF", u"Afghanistan"), - (u"AX", u"\xc5land Islands"), - (u"AL", u"Albania"), - (u"DZ", u"Algeria"), - (u"AS", u"American Samoa"), - (u"AD", u"Andorra"), - (u"AO", u"Angola"), - (u"AI", u"Anguilla"), - (u"AQ", u"Antarctica"), - (u"AG", u"Antigua and Barbuda"), - (u"AR", u"Argentina"), - (u"AM", u"Armenia"), - (u"AW", u"Aruba"), - (u"AU", u"Australia"), - (u"AT", u"Austria"), - (u"AZ", u"Azerbaijan"), - (u"BS", u"Bahamas"), - (u"BH", u"Bahrain"), - (u"BD", u"Bangladesh"), - (u"BB", u"Barbados"), - (u"BY", u"Belarus"), - (u"BE", u"Belgium"), - (u"BZ", u"Belize"), - (u"BJ", u"Benin"), - (u"BM", u"Bermuda"), - (u"BT", u"Bhutan"), - (u"BO", u"Bolivia"), - (u"BQ", u"Bonaire, Sint Eustatius and Saba"), - (u"BA", u"Bosnia and Herzegovina"), - (u"BW", u"Botswana"), - (u"BV", u"Bouvet Island"), - (u"BR", u"Brazil"), - (u"IO", u"British Indian Ocean Territory"), - (u"BN", u"Brunei"), - (u"BG", u"Bulgaria"), - (u"BF", u"Burkina Faso"), - (u"BI", u"Burundi"), - (u"CV", u"Cabo Verde"), - (u"KH", u"Cambodia"), - (u"CM", u"Cameroon"), - (u"CA", u"Canada"), - (u"KY", u"Cayman Islands"), - (u"CF", u"Central African Republic"), - (u"TD", u"Chad"), - (u"CL", u"Chile"), - (u"CN", u"China"), - (u"CX", u"Christmas Island"), - (u"CC", u"Cocos (Keeling) Islands"), - (u"CO", u"Colombia"), - (u"KM", u"Comoros"), - (u"CG", u"Congo"), - (u"CD", u"Congo (the Democratic Republic of the)"), - (u"CK", u"Cook Islands"), - (u"CR", u"Costa Rica"), - (u"CI", u"C\xf4te d'Ivoire"), - (u"HR", u"Croatia"), - (u"CU", u"Cuba"), - (u"CW", u"Cura\xe7ao"), - (u"CY", u"Cyprus"), - (u"CZ", u"Czechia"), - (u"DK", u"Denmark"), - (u"DJ", u"Djibouti"), - (u"DM", u"Dominica"), - (u"DO", u"Dominican Republic"), - (u"EC", u"Ecuador"), - (u"EG", u"Egypt"), - (u"SV", u"El Salvador"), - (u"GQ", u"Equatorial Guinea"), - (u"ER", u"Eritrea"), - (u"EE", u"Estonia"), - (u"SZ", u"Eswatini"), - (u"ET", u"Ethiopia"), - (u"FK", u"Falkland Islands [Malvinas]"), - (u"FO", u"Faroe Islands"), - (u"FJ", u"Fiji"), - (u"FI", u"Finland"), - (u"FR", u"France"), - (u"GF", u"French Guiana"), - (u"PF", u"French Polynesia"), - (u"TF", u"French Southern Territories"), - (u"GA", u"Gabon"), - (u"GM", u"Gambia"), - (u"GE", u"Georgia"), - (u"DE", u"Germany"), - (u"GH", u"Ghana"), - (u"GI", u"Gibraltar"), - (u"GR", u"Greece"), - (u"GL", u"Greenland"), - (u"GD", u"Grenada"), - (u"GP", u"Guadeloupe"), - (u"GU", u"Guam"), - (u"GT", u"Guatemala"), - (u"GG", u"Guernsey"), - (u"GN", u"Guinea"), - (u"GW", u"Guinea-Bissau"), - (u"GY", u"Guyana"), - (u"HT", u"Haiti"), - (u"HM", u"Heard Island and McDonald Islands"), - (u"VA", u"Holy See"), - (u"HN", u"Honduras"), - (u"HK", u"Hong Kong"), - (u"HU", u"Hungary"), - (u"IS", u"Iceland"), - (u"IN", u"India"), - (u"ID", u"Indonesia"), - (u"IR", u"Iran"), - (u"IQ", u"Iraq"), - (u"IE", u"Ireland"), - (u"IM", u"Isle of Man"), - (u"IL", u"Israel"), - (u"IT", u"Italy"), - (u"JM", u"Jamaica"), - (u"JP", u"Japan"), - (u"JE", u"Jersey"), - (u"JO", u"Jordan"), - (u"KZ", u"Kazakhstan"), - (u"KE", u"Kenya"), - (u"KI", u"Kiribati"), - (u"XK", u"Kosovo"), - (u"KW", u"Kuwait"), - (u"KG", u"Kyrgyzstan"), - (u"LA", u"Laos"), - (u"LV", u"Latvia"), - (u"LB", u"Lebanon"), - (u"LS", u"Lesotho"), - (u"LR", u"Liberia"), - (u"LY", u"Libya"), - (u"LI", u"Liechtenstein"), - (u"LT", u"Lithuania"), - (u"LU", u"Luxembourg"), - (u"MO", u"Macao"), - (u"MG", u"Madagascar"), - (u"MW", u"Malawi"), - (u"MY", u"Malaysia"), - (u"MV", u"Maldives"), - (u"ML", u"Mali"), - (u"MT", u"Malta"), - (u"MH", u"Marshall Islands"), - (u"MQ", u"Martinique"), - (u"MR", u"Mauritania"), - (u"MU", u"Mauritius"), - (u"YT", u"Mayotte"), - (u"MX", u"Mexico"), - (u"FM", u"Micronesia (Federated States of)"), - (u"MD", u"Moldova"), - (u"MC", u"Monaco"), - (u"MN", u"Mongolia"), - (u"ME", u"Montenegro"), - (u"MS", u"Montserrat"), - (u"MA", u"Morocco"), - (u"MZ", u"Mozambique"), - (u"MM", u"Myanmar"), - (u"NA", u"Namibia"), - (u"NR", u"Nauru"), - (u"NP", u"Nepal"), - (u"NL", u"Netherlands"), - (u"NC", u"New Caledonia"), - (u"NZ", u"New Zealand"), - (u"NI", u"Nicaragua"), - (u"NE", u"Niger"), - (u"NG", u"Nigeria"), - (u"NU", u"Niue"), - (u"NF", u"Norfolk Island"), - (u"KP", u"North Korea"), - (u"MK", u"North Macedonia"), - (u"MP", u"Northern Mariana Islands"), - (u"NO", u"Norway"), - (u"OM", u"Oman"), - (u"PK", u"Pakistan"), - (u"PW", u"Palau"), - (u"PS", u"Palestine, State of"), - (u"PA", u"Panama"), - (u"PG", u"Papua New Guinea"), - (u"PY", u"Paraguay"), - (u"PE", u"Peru"), - (u"PH", u"Philippines"), - (u"PN", u"Pitcairn"), - (u"PL", u"Poland"), - (u"PT", u"Portugal"), - (u"PR", u"Puerto Rico"), - (u"QA", u"Qatar"), - (u"RE", u"R\xe9union"), - (u"RO", u"Romania"), - (u"RU", u"Russia"), - (u"RW", u"Rwanda"), - (u"BL", u"Saint Barth\xe9lemy"), - (u"SH", u"Saint Helena, Ascension and Tristan da Cunha"), - (u"KN", u"Saint Kitts and Nevis"), - (u"LC", u"Saint Lucia"), - (u"MF", u"Saint Martin (French part)"), - (u"PM", u"Saint Pierre and Miquelon"), - (u"VC", u"Saint Vincent and the Grenadines"), - (u"WS", u"Samoa"), - (u"SM", u"San Marino"), - (u"ST", u"Sao Tome and Principe"), - (u"SA", u"Saudi Arabia"), - (u"SN", u"Senegal"), - (u"RS", u"Serbia"), - (u"SC", u"Seychelles"), - (u"SL", u"Sierra Leone"), - (u"SG", u"Singapore"), - (u"SX", u"Sint Maarten (Dutch part)"), - (u"SK", u"Slovakia"), - (u"SI", u"Slovenia"), - (u"SB", u"Solomon Islands"), - (u"SO", u"Somalia"), - (u"ZA", u"South Africa"), - (u"GS", u"South Georgia and the South Sandwich Islands"), - (u"KR", u"South Korea"), - (u"SS", u"South Sudan"), - (u"ES", u"Spain"), - (u"LK", u"Sri Lanka"), - (u"SD", u"Sudan"), - (u"SR", u"Suriname"), - (u"SJ", u"Svalbard and Jan Mayen"), - (u"SE", u"Sweden"), - (u"CH", u"Switzerland"), - (u"SY", u"Syria"), - (u"TW", u"Taiwan"), - (u"TJ", u"Tajikistan"), - (u"TZ", u"Tanzania"), - (u"TH", u"Thailand"), - (u"TL", u"Timor-Leste"), - (u"TG", u"Togo"), - (u"TK", u"Tokelau"), - (u"TO", u"Tonga"), - (u"TT", u"Trinidad and Tobago"), - (u"TN", u"Tunisia"), - (u"TR", u"Turkey"), - (u"TM", u"Turkmenistan"), - (u"TC", u"Turks and Caicos Islands"), - (u"TV", u"Tuvalu"), - (u"UG", u"Uganda"), - (u"UA", u"Ukraine"), - (u"AE", u"United Arab Emirates"), - (u"GB", u"United Kingdom"), - (u"UM", u"United States Minor Outlying Islands"), - (u"US", u"United States of America"), - (u"UY", u"Uruguay"), - (u"UZ", u"Uzbekistan"), - (u"VU", u"Vanuatu"), - (u"VE", u"Venezuela"), - (u"VN", u"Vietnam"), - (u"VG", u"Virgin Islands (British)"), - (u"VI", u"Virgin Islands (U.S.)"), - (u"WF", u"Wallis and Futuna"), - (u"EH", u"Western Sahara"), - (u"YE", u"Yemen"), - (u"ZM", u"Zambia"), - (u"ZW", u"Zimbabwe") + ("AF", "Afghanistan"), + ("AX", "\xc5land Islands"), + ("AL", "Albania"), + ("DZ", "Algeria"), + ("AS", "American Samoa"), + ("AD", "Andorra"), + ("AO", "Angola"), + ("AI", "Anguilla"), + ("AQ", "Antarctica"), + ("AG", "Antigua and Barbuda"), + ("AR", "Argentina"), + ("AM", "Armenia"), + ("AW", "Aruba"), + ("AU", "Australia"), + ("AT", "Austria"), + ("AZ", "Azerbaijan"), + ("BS", "Bahamas"), + ("BH", "Bahrain"), + ("BD", "Bangladesh"), + ("BB", "Barbados"), + ("BY", "Belarus"), + ("BE", "Belgium"), + ("BZ", "Belize"), + ("BJ", "Benin"), + ("BM", "Bermuda"), + ("BT", "Bhutan"), + ("BO", "Bolivia"), + ("BQ", "Bonaire, Sint Eustatius and Saba"), + ("BA", "Bosnia and Herzegovina"), + ("BW", "Botswana"), + ("BV", "Bouvet Island"), + ("BR", "Brazil"), + ("IO", "British Indian Ocean Territory"), + ("BN", "Brunei"), + ("BG", "Bulgaria"), + ("BF", "Burkina Faso"), + ("BI", "Burundi"), + ("CV", "Cabo Verde"), + ("KH", "Cambodia"), + ("CM", "Cameroon"), + ("CA", "Canada"), + ("KY", "Cayman Islands"), + ("CF", "Central African Republic"), + ("TD", "Chad"), + ("CL", "Chile"), + ("CN", "China"), + ("CX", "Christmas Island"), + ("CC", "Cocos (Keeling) Islands"), + ("CO", "Colombia"), + ("KM", "Comoros"), + ("CG", "Congo"), + ("CD", "Congo (the Democratic Republic of the)"), + ("CK", "Cook Islands"), + ("CR", "Costa Rica"), + ("CI", "C\xf4te d'Ivoire"), + ("HR", "Croatia"), + ("CU", "Cuba"), + ("CW", "Cura\xe7ao"), + ("CY", "Cyprus"), + ("CZ", "Czechia"), + ("DK", "Denmark"), + ("DJ", "Djibouti"), + ("DM", "Dominica"), + ("DO", "Dominican Republic"), + ("EC", "Ecuador"), + ("EG", "Egypt"), + ("SV", "El Salvador"), + ("GQ", "Equatorial Guinea"), + ("ER", "Eritrea"), + ("EE", "Estonia"), + ("SZ", "Eswatini"), + ("ET", "Ethiopia"), + ("FK", "Falkland Islands [Malvinas]"), + ("FO", "Faroe Islands"), + ("FJ", "Fiji"), + ("FI", "Finland"), + ("FR", "France"), + ("GF", "French Guiana"), + ("PF", "French Polynesia"), + ("TF", "French Southern Territories"), + ("GA", "Gabon"), + ("GM", "Gambia"), + ("GE", "Georgia"), + ("DE", "Germany"), + ("GH", "Ghana"), + ("GI", "Gibraltar"), + ("GR", "Greece"), + ("GL", "Greenland"), + ("GD", "Grenada"), + ("GP", "Guadeloupe"), + ("GU", "Guam"), + ("GT", "Guatemala"), + ("GG", "Guernsey"), + ("GN", "Guinea"), + ("GW", "Guinea-Bissau"), + ("GY", "Guyana"), + ("HT", "Haiti"), + ("HM", "Heard Island and McDonald Islands"), + ("VA", "Holy See"), + ("HN", "Honduras"), + ("HK", "Hong Kong"), + ("HU", "Hungary"), + ("IS", "Iceland"), + ("IN", "India"), + ("ID", "Indonesia"), + ("IR", "Iran"), + ("IQ", "Iraq"), + ("IE", "Ireland"), + ("IM", "Isle of Man"), + ("IL", "Israel"), + ("IT", "Italy"), + ("JM", "Jamaica"), + ("JP", "Japan"), + ("JE", "Jersey"), + ("JO", "Jordan"), + ("KZ", "Kazakhstan"), + ("KE", "Kenya"), + ("KI", "Kiribati"), + ("XK", "Kosovo"), + ("KW", "Kuwait"), + ("KG", "Kyrgyzstan"), + ("LA", "Laos"), + ("LV", "Latvia"), + ("LB", "Lebanon"), + ("LS", "Lesotho"), + ("LR", "Liberia"), + ("LY", "Libya"), + ("LI", "Liechtenstein"), + ("LT", "Lithuania"), + ("LU", "Luxembourg"), + ("MO", "Macao"), + ("MG", "Madagascar"), + ("MW", "Malawi"), + ("MY", "Malaysia"), + ("MV", "Maldives"), + ("ML", "Mali"), + ("MT", "Malta"), + ("MH", "Marshall Islands"), + ("MQ", "Martinique"), + ("MR", "Mauritania"), + ("MU", "Mauritius"), + ("YT", "Mayotte"), + ("MX", "Mexico"), + ("FM", "Micronesia (Federated States of)"), + ("MD", "Moldova"), + ("MC", "Monaco"), + ("MN", "Mongolia"), + ("ME", "Montenegro"), + ("MS", "Montserrat"), + ("MA", "Morocco"), + ("MZ", "Mozambique"), + ("MM", "Myanmar"), + ("NA", "Namibia"), + ("NR", "Nauru"), + ("NP", "Nepal"), + ("NL", "Netherlands"), + ("NC", "New Caledonia"), + ("NZ", "New Zealand"), + ("NI", "Nicaragua"), + ("NE", "Niger"), + ("NG", "Nigeria"), + ("NU", "Niue"), + ("NF", "Norfolk Island"), + ("KP", "North Korea"), + ("MK", "North Macedonia"), + ("MP", "Northern Mariana Islands"), + ("NO", "Norway"), + ("OM", "Oman"), + ("PK", "Pakistan"), + ("PW", "Palau"), + ("PS", "Palestine, State of"), + ("PA", "Panama"), + ("PG", "Papua New Guinea"), + ("PY", "Paraguay"), + ("PE", "Peru"), + ("PH", "Philippines"), + ("PN", "Pitcairn"), + ("PL", "Poland"), + ("PT", "Portugal"), + ("PR", "Puerto Rico"), + ("QA", "Qatar"), + ("RE", "R\xe9union"), + ("RO", "Romania"), + ("RU", "Russia"), + ("RW", "Rwanda"), + ("BL", "Saint Barth\xe9lemy"), + ("SH", "Saint Helena, Ascension and Tristan da Cunha"), + ("KN", "Saint Kitts and Nevis"), + ("LC", "Saint Lucia"), + ("MF", "Saint Martin (French part)"), + ("PM", "Saint Pierre and Miquelon"), + ("VC", "Saint Vincent and the Grenadines"), + ("WS", "Samoa"), + ("SM", "San Marino"), + ("ST", "Sao Tome and Principe"), + ("SA", "Saudi Arabia"), + ("SN", "Senegal"), + ("RS", "Serbia"), + ("SC", "Seychelles"), + ("SL", "Sierra Leone"), + ("SG", "Singapore"), + ("SX", "Sint Maarten (Dutch part)"), + ("SK", "Slovakia"), + ("SI", "Slovenia"), + ("SB", "Solomon Islands"), + ("SO", "Somalia"), + ("ZA", "South Africa"), + ("GS", "South Georgia and the South Sandwich Islands"), + ("KR", "South Korea"), + ("SS", "South Sudan"), + ("ES", "Spain"), + ("LK", "Sri Lanka"), + ("SD", "Sudan"), + ("SR", "Suriname"), + ("SJ", "Svalbard and Jan Mayen"), + ("SE", "Sweden"), + ("CH", "Switzerland"), + ("SY", "Syria"), + ("TW", "Taiwan"), + ("TJ", "Tajikistan"), + ("TZ", "Tanzania"), + ("TH", "Thailand"), + ("TL", "Timor-Leste"), + ("TG", "Togo"), + ("TK", "Tokelau"), + ("TO", "Tonga"), + ("TT", "Trinidad and Tobago"), + ("TN", "Tunisia"), + ("TR", "Turkey"), + ("TM", "Turkmenistan"), + ("TC", "Turks and Caicos Islands"), + ("TV", "Tuvalu"), + ("UG", "Uganda"), + ("UA", "Ukraine"), + ("AE", "United Arab Emirates"), + ("GB", "United Kingdom"), + ("UM", "United States Minor Outlying Islands"), + ("US", "United States of America"), + ("UY", "Uruguay"), + ("UZ", "Uzbekistan"), + ("VU", "Vanuatu"), + ("VE", "Venezuela"), + ("VN", "Vietnam"), + ("VG", "Virgin Islands (British)"), + ("VI", "Virgin Islands (U.S.)"), + ("WF", "Wallis and Futuna"), + ("EH", "Western Sahara"), + ("YE", "Yemen"), + ("ZM", "Zambia"), + ("ZW", "Zimbabwe") ] diff --git a/openedx/core/djangoapps/user_api/tests/test_helpers.py b/openedx/core/djangoapps/user_api/tests/test_helpers.py index 73e20a7c6f..958bb3fb4e 100644 --- a/openedx/core/djangoapps/user_api/tests/test_helpers.py +++ b/openedx/core/djangoapps/user_api/tests/test_helpers.py @@ -5,13 +5,12 @@ Tests for helper functions. import json import re +from unittest import mock import ddt # lint-amnesty, pylint: disable=unused-import -import mock import pytest from django import forms from django.test import TestCase -from six import text_type from ..helpers import FormDescription, InvalidFieldError, intercept_errors @@ -57,17 +56,17 @@ class InterceptErrorsTest(TestCase): self.maxDiff = None exception = 'openedx.core.djangoapps.user_api.tests.test_helpers.FakeInputException' expected_log_msg = ( - u"An unexpected error occurred when calling 'intercepted_function' with arguments '()' and " - u"keyword arguments '{{'raise_error': }}' " - u"from File \"{}\", line XXX, in test_logs_errors\n" - u" intercepted_function(raise_error=FakeInputException): FakeInputException()" + "An unexpected error occurred when calling 'intercepted_function' with arguments '()' and " + "keyword arguments '{{'raise_error': }}' " + "from File \"{}\", line XXX, in test_logs_errors\n" + " intercepted_function(raise_error=FakeInputException): FakeInputException()" ).format(exception, __file__.rstrip('c')) # Verify that the raised exception has the error message try: intercepted_function(raise_error=FakeInputException) except FakeOutputException as ex: - actual_message = re.sub(r'line \d+', 'line XXX', text_type(ex), flags=re.MULTILINE) + actual_message = re.sub(r'line \d+', 'line XXX', str(ex), flags=re.MULTILINE) assert actual_message == expected_log_msg # Verify that the error logger is called @@ -159,7 +158,7 @@ class FormDescriptionTest(TestCase): {'default': True, 'name': 'Pakistan', 'value': 'PK'}] -class DummyRegistrationExtensionModel(object): +class DummyRegistrationExtensionModel: """ Dummy registration object """ @@ -190,8 +189,8 @@ class TestCaseForm(forms.Form): favorite_movie = forms.CharField( label="Fav Flick", min_length=MOVIE_MIN_LEN, max_length=MOVIE_MAX_LEN, error_messages={ - "required": u"Please tell us your favorite movie.", - "invalid": u"We're pretty sure you made that movie up." + "required": "Please tell us your favorite movie.", + "invalid": "We're pretty sure you made that movie up." } ) favorite_editor = forms.ChoiceField(label="Favorite Editor", choices=FAVORITE_EDITOR, required=False, initial='cat') @@ -207,7 +206,7 @@ class TestCaseForm(forms.Form): dummy_model = DummyRegistrationExtensionModel() return dummy_model - class Meta(object): + class Meta: """ Set options for fields which can't be conveyed in their definition. """ diff --git a/openedx/core/djangoapps/user_api/tests/test_middleware.py b/openedx/core/djangoapps/user_api/tests/test_middleware.py index 8f9686beca..a5794f17ab 100644 --- a/openedx/core/djangoapps/user_api/tests/test_middleware.py +++ b/openedx/core/djangoapps/user_api/tests/test_middleware.py @@ -1,10 +1,10 @@ """Tests for user API middleware""" +from unittest.mock import Mock, patch from django.http import HttpResponse from django.test import TestCase from django.test.client import RequestFactory -from mock import Mock, patch from common.djangoapps.student.tests.factories import AnonymousUserFactory, UserFactory @@ -17,7 +17,7 @@ class TagsMiddlewareTest(TestCase): Test the UserTagsEventContextMiddleware """ def setUp(self): - super(TagsMiddlewareTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.middleware = UserTagsEventContextMiddleware() self.user = UserFactory.create() self.other_user = UserFactory.create() @@ -27,7 +27,7 @@ class TagsMiddlewareTest(TestCase): # TODO: Make it so we can use reverse. Appears to fail depending on the order in which tests are run #self.request = RequestFactory().get(reverse('courseware', kwargs={'course_id': self.course_id})) - self.request = RequestFactory().get('/courses/{}/courseware'.format(self.course_id)) + self.request = RequestFactory().get(f'/courses/{self.course_id}/courseware') self.request.user = self.user self.response = Mock(spec=HttpResponse) diff --git a/openedx/core/djangoapps/user_api/tests/test_models.py b/openedx/core/djangoapps/user_api/tests/test_models.py index 1246b47a05..6a5d64bd24 100644 --- a/openedx/core/djangoapps/user_api/tests/test_models.py +++ b/openedx/core/djangoapps/user_api/tests/test_models.py @@ -123,7 +123,7 @@ class TestUserPreferenceEvents(UserSettingsEventTestMixin, TestCase): Mixin for verifying that user preference events are fired correctly. """ def setUp(self): - super(TestUserPreferenceEvents, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.table = "user_api_userpreference" self.user = UserFactory.create() self.TEST_KEY = "test key" diff --git a/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py b/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py index 5168276030..62ba46ddc3 100644 --- a/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py +++ b/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py @@ -4,11 +4,10 @@ Test the user api's partition extensions. from collections import defaultdict +from unittest.mock import patch import pytest from django.test import TestCase -from mock import patch -from six.moves import range from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme, UserPartitionError from common.djangoapps.student.tests.factories import UserFactory @@ -16,7 +15,7 @@ from xmodule.partitions.partitions import Group, UserPartition from xmodule.partitions.tests.test_partitions import PartitionTestCase -class MemoryCourseTagAPI(object): +class MemoryCourseTagAPI: """ An implementation of a user service that uses an in-memory dictionary for storage """ @@ -31,7 +30,7 @@ class MemoryCourseTagAPI(object): """Gets the value of ``key``""" self._tags[course_id][key] = value - class BulkCourseTags(object): + class BulkCourseTags: @classmethod def is_prefetched(self, course_id): # lint-amnesty, pylint: disable=bad-classmethod-argument, unused-argument return False @@ -46,7 +45,7 @@ class TestRandomUserPartitionScheme(PartitionTestCase): MOCK_COURSE_ID = "mock-course-id" def setUp(self): - super(TestRandomUserPartitionScheme, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() # Patch in a memory-based user service instead of using the persistent version course_tag_api = MemoryCourseTagAPI() self.user_service_patcher = patch( diff --git a/openedx/core/djangoapps/user_api/tests/test_views.py b/openedx/core/djangoapps/user_api/tests/test_views.py index 4edabd1f4a..6d615adf9b 100644 --- a/openedx/core/djangoapps/user_api/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/tests/test_views.py @@ -3,11 +3,10 @@ import json # lint-amnesty, pylint: disable=unused-import from unittest import skipUnless # lint-amnesty, pylint: disable=unused-import +from unittest import mock # lint-amnesty, pylint: disable=unused-import import pytest import ddt import httpretty # lint-amnesty, pylint: disable=unused-import -import mock # lint-amnesty, pylint: disable=unused-import -import six from django.conf import settings # lint-amnesty, pylint: disable=unused-import from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user, unused-import from django.core import mail # lint-amnesty, pylint: disable=unused-import @@ -17,8 +16,6 @@ from django.test.utils import override_settings from django.urls import reverse from opaque_keys.edx.keys import CourseKey from pytz import UTC, common_timezones_set # lint-amnesty, pylint: disable=unused-import -from six import text_type -from six.moves import range from social_django.models import Partial, UserSocialAuth # lint-amnesty, pylint: disable=unused-import from openedx.core.djangoapps.django_comment_common import models @@ -90,9 +87,8 @@ class UserAPITestCase(ApiTestCase): def assertUserIsValid(self, user): """Assert that the given user result is valid""" - six.assertCountEqual(self, list(user.keys()), ["email", "id", "name", "username", "preferences", "url"]) - six.assertCountEqual( - self, + self.assertCountEqual(list(user.keys()), ["email", "id", "name", "username", "preferences", "url"]) + self.assertCountEqual( list(user["preferences"].items()), [(pref.key, pref.value) for pref in self.prefs if pref.user.id == user["id"]] # lint-amnesty, pylint: disable=no-member ) @@ -102,7 +98,7 @@ class UserAPITestCase(ApiTestCase): """ Assert that the given preference is acknowledged by the system """ - six.assertCountEqual(self, list(pref.keys()), ["user", "key", "value", "url"]) + self.assertCountEqual(list(pref.keys()), ["user", "key", "value", "url"]) self.assertSelfReferential(pref) self.assertUserIsValid(pref["user"]) @@ -124,7 +120,7 @@ class EmptyUserTestCase(UserAPITestCase): class EmptyRoleTestCase(UserAPITestCase): """Test that the endpoint supports empty result sets""" course_id = CourseKey.from_string("org/course/run") - LIST_URI = ROLE_LIST_URI + "?course_id=" + six.text_type(course_id) + LIST_URI = ROLE_LIST_URI + "?course_id=" + str(course_id) def test_get_list_empty(self): """Test that the endpoint properly returns empty result sets""" @@ -140,11 +136,11 @@ class UserApiTestCase(UserAPITestCase): Generalized test case class for specific implementations below """ def setUp(self): - super(UserApiTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.users = [ UserFactory.create( - email="test{0}@test.org".format(i), - profile__name=u"Test {0}".format(i) + email=f"test{i}@test.org", + profile__name=f"Test {i}" ) for i in range(5) ] @@ -161,10 +157,10 @@ class RoleTestCase(UserApiTestCase): Test cases covering Role-related views and their behaviors """ course_id = CourseKey.from_string("org/course/run") - LIST_URI = ROLE_LIST_URI + "?course_id=" + six.text_type(course_id) + LIST_URI = ROLE_LIST_URI + "?course_id=" + str(course_id) def setUp(self): - super(RoleTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() (role, _) = models.Role.objects.get_or_create( name=models.FORUM_ROLE_MODERATOR, course_id=self.course_id @@ -222,7 +218,7 @@ class RoleTestCase(UserApiTestCase): def test_get_list_pagination(self): first_page = self.get_json(self.LIST_URI, data={ "page_size": 3, - "course_id": text_type(self.course_id), + "course_id": str(self.course_id), }) assert first_page['count'] == 5 first_page_next_uri = first_page["next"] @@ -253,7 +249,7 @@ class UserViewSetTest(UserApiTestCase): LIST_URI = USER_LIST_URI def setUp(self): - super(UserViewSetTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.detail_uri = self.get_uri_for_user(self.users[0]) # List view tests @@ -362,7 +358,7 @@ class UserPreferenceViewSetTest(CacheIsolationTestCase, UserApiTestCase): ENABLED_CACHES = ['default'] def setUp(self): - super(UserPreferenceViewSetTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.detail_uri = self.get_uri_for_pref(self.prefs[0]) # List view tests @@ -555,30 +551,30 @@ class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase): @classmethod def setUpClass(cls): - super(UpdateEmailOptInTestCase, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create() cls.url = reverse("preferences_email_opt_in") def setUp(self): """ Create a course and user, then log in. """ - super(UpdateEmailOptInTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD) self.client.login(username=self.USERNAME, password=self.PASSWORD) @ddt.data( - (u"True", u"True"), - (u"true", u"True"), - (u"TrUe", u"True"), - (u"Banana", u"False"), - (u"strawberries", u"False"), - (u"False", u"False"), + ("True", "True"), + ("true", "True"), + ("TrUe", "True"), + ("Banana", "False"), + ("strawberries", "False"), + ("False", "False"), ) @ddt.unpack def test_update_email_opt_in(self, opt, result): """Tests the email opt in preference""" # Register, which should trigger an activation email response = self.client.post(self.url, { - "course_id": six.text_type(self.course.id), + "course_id": str(self.course.id), "email_opt_in": opt }) self.assertHttpOK(response) @@ -597,9 +593,9 @@ class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase): """Tests the email opt in preference""" params = {} if use_course_id: - params["course_id"] = six.text_type(self.course.id) + params["course_id"] = str(self.course.id) if use_opt_in: - params["email_opt_in"] = u"True" + params["email_opt_in"] = "True" response = self.client.post(self.url, params) self.assertHttpBadRequest(response) @@ -610,14 +606,14 @@ class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase): self.user.save() # Register, which should trigger an activation email response = self.client.post(self.url, { - "course_id": six.text_type(self.course.id), - "email_opt_in": u"True" + "course_id": str(self.course.id), + "email_opt_in": "True" }) self.assertHttpOK(response) preference = UserOrgTag.objects.get( user=self.user, org=self.course.id.org, key="email-optin" ) - assert preference.value == u'True' + assert preference.value == 'True' def test_update_email_opt_in_anonymous_user(self): """ @@ -626,8 +622,8 @@ class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase): """ self.client.logout() response = self.client.post(self.url, { - "course_id": six.text_type(self.course.id), - "email_opt_in": u"True" + "course_id": str(self.course.id), + "email_opt_in": "True" }) assert response.status_code == 403 @@ -638,7 +634,7 @@ class UpdateEmailOptInTestCase(UserAPITestCase, SharedModuleStoreTestCase): """ response = self.client.post(self.url, { "course_id": 'invalid', - "email_opt_in": u"True" + "email_opt_in": "True" }) self.assertHttpBadRequest(response) with pytest.raises(UserOrgTag.DoesNotExist): diff --git a/openedx/core/djangoapps/user_api/urls.py b/openedx/core/djangoapps/user_api/urls.py index 41b1bb08ff..ba8ee9a7e1 100644 --- a/openedx/core/djangoapps/user_api/urls.py +++ b/openedx/core/djangoapps/user_api/urls.py @@ -89,17 +89,17 @@ urlpatterns = [ name='accounts_detail_api' ), url( - r'^v1/accounts/{}$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}$', ACCOUNT_DETAIL, name='accounts_api' ), url( - r'^v1/accounts/{}/image$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}/image$', ProfileImageView.as_view(), name='accounts_profile_image_api' ), url( - r'^v1/accounts/{}/deactivate/$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}/deactivate/$', AccountDeactivationView.as_view(), name='accounts_deactivation' ), @@ -109,17 +109,17 @@ urlpatterns = [ name='deactivate_logout' ), url( - r'^v1/accounts/{}/verification_status/$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}/verification_status/$', IDVerificationStatusView.as_view(), name='verification_status' ), url( - r'^v1/accounts/{}/verifications/$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}/verifications/$', IDVerificationStatusDetailsView.as_view(), name='verification_details' ), url( - r'^v1/accounts/{}/retirement_status/$'.format(settings.USERNAME_PATTERN), + fr'^v1/accounts/{settings.USERNAME_PATTERN}/retirement_status/$', RETIREMENT_RETRIEVE, name='accounts_retirement_retrieve' ), @@ -169,12 +169,12 @@ urlpatterns = [ name='username_replacement' ), url( - r'^v1/preferences/{}$'.format(settings.USERNAME_PATTERN), + fr'^v1/preferences/{settings.USERNAME_PATTERN}$', PreferencesView.as_view(), name='preferences_api' ), url( - r'^v1/preferences/{}/(?P[a-zA-Z0-9_]+)$'.format(settings.USERNAME_PATTERN), + fr'^v1/preferences/{settings.USERNAME_PATTERN}/(?P[a-zA-Z0-9_]+)$', PreferencesDetailView.as_view(), name='preferences_detail_api' ), @@ -183,7 +183,7 @@ urlpatterns = [ # Moved from user_api/legacy_urls.py url( - r'^v1/preferences/(?P{})/users/$'.format(UserPreference.KEY_REGEX), + fr'^v1/preferences/(?P{UserPreference.KEY_REGEX})/users/$', user_api_views.PreferenceUsersListView.as_view() ), diff --git a/openedx/core/djangoapps/user_api/views.py b/openedx/core/djangoapps/user_api/views.py index e4b75c9b31..1870115e57 100644 --- a/openedx/core/djangoapps/user_api/views.py +++ b/openedx/core/djangoapps/user_api/views.py @@ -17,7 +17,6 @@ from rest_framework import authentication, generics, status, viewsets from rest_framework.exceptions import ParseError from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView -from six import text_type # lint-amnesty, pylint: disable=unused-import from openedx.core.djangoapps.django_comment_common.models import Role from openedx.core.djangoapps.user_api import accounts # lint-amnesty, pylint: disable=unused-import @@ -128,7 +127,7 @@ class UpdateEmailOptInPreference(APIView): except InvalidKeyError: return HttpResponse( status=400, - content=u"No course '{course_id}' found".format(course_id=course_id), + content=f"No course '{course_id}' found", content_type="text/plain" ) # Only check for true. All other values are False.