From 91ea53da565e46f9d1ce854adfb9dd58f1d314cb Mon Sep 17 00:00:00 2001 From: Usama Sadiq Date: Thu, 11 Mar 2021 17:26:03 +0500 Subject: [PATCH] refactor: Ran pyupgrade on openedx/core/djangoapps/user_api --- .../accounts/tests/retirement_helpers.py | 46 +++++------ .../user_api/accounts/tests/test_api.py | 38 ++++----- .../accounts/tests/test_image_helpers.py | 9 +- .../accounts/tests/test_permissions.py | 4 +- .../accounts/tests/test_retirement_views.py | 46 +++++------ .../accounts/tests/test_serializers.py | 2 +- .../accounts/tests/test_settings_views.py | 5 +- .../user_api/accounts/tests/test_utils.py | 6 +- .../user_api/accounts/tests/test_views.py | 82 +++++++++---------- .../user_api/accounts/tests/testutils.py | 61 +++++++------- .../user_api/course_tag/tests/test_api.py | 2 +- .../commands/bulk_user_org_email_optout.py | 17 ++-- .../cancel_user_retirement_request.py | 8 +- .../management/commands/email_opt_in_list.py | 30 +++---- .../commands/migrate_user_profile_langs.py | 12 +-- .../commands/populate_retirement_states.py | 18 ++-- .../management/commands/retire_user.py | 6 +- .../commands/sync_hubspot_contacts.py | 34 ++++---- .../tests/test_bulk_user_org_email_optout.py | 12 +-- .../tests/test_email_opt_in_list.py | 34 +++----- .../tests/test_populate_retirement_states.py | 2 +- .../tests/test_sync_hubspot_contacts.py | 17 ++-- .../user_api/preferences/tests/test_api.py | 61 +++++++------- .../user_api/preferences/tests/test_views.py | 55 ++++++------- .../verification_api/tests/test_views.py | 26 +++--- 25 files changed, 298 insertions(+), 335 deletions(-) diff --git a/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py b/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py index fffe2a2f1e..1828c63e1f 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/retirement_helpers.py @@ -114,33 +114,33 @@ class RetirementTestCase(TestCase): `responses` for performance reasons. """ retirement_dict = { - u'id': retirement.id, - u'user': { - u'id': retirement.user.id, - u'username': retirement.user.username, - u'email': retirement.user.email, - u'profile': { - u'id': retirement.user.profile.id, - u'name': retirement.user.profile.name + 'id': retirement.id, + 'user': { + 'id': retirement.user.id, + 'username': retirement.user.username, + 'email': retirement.user.email, + 'profile': { + 'id': retirement.user.profile.id, + 'name': retirement.user.profile.name }, }, - u'original_username': retirement.original_username, - u'original_email': retirement.original_email, - u'original_name': retirement.original_name, - u'retired_username': retirement.retired_username, - u'retired_email': retirement.retired_email, - u'current_state': { - u'id': retirement.current_state.id, - u'state_name': retirement.current_state.state_name, - u'state_execution_order': retirement.current_state.state_execution_order, + 'original_username': retirement.original_username, + 'original_email': retirement.original_email, + 'original_name': retirement.original_name, + 'retired_username': retirement.retired_username, + 'retired_email': retirement.retired_email, + 'current_state': { + 'id': retirement.current_state.id, + 'state_name': retirement.current_state.state_name, + 'state_execution_order': retirement.current_state.state_execution_order, }, - u'last_state': { - u'id': retirement.last_state.id, - u'state_name': retirement.last_state.state_name, - u'state_execution_order': retirement.last_state.state_execution_order, + 'last_state': { + 'id': retirement.last_state.id, + 'state_name': retirement.last_state.state_name, + 'state_execution_order': retirement.last_state.state_execution_order, }, - u'created': retirement.created, - u'modified': retirement.modified + 'created': retirement.created, + 'modified': retirement.modified } if all_fields: diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py index d7a6c6860b..8677a477e1 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Unit tests for behavior that is specific to the api methods (vs. the view methods). Most of the functionality is covered in test_views.py. @@ -7,6 +6,7 @@ Most of the functionality is covered in test_views.py. import itertools import unicodedata +from unittest.mock import Mock, patch import pytest import ddt from django.conf import settings @@ -16,8 +16,6 @@ from django.http import HttpResponse from django.test import TestCase from django.test.client import RequestFactory from django.urls import reverse -from mock import Mock, patch -from six import iteritems from social_django.models import UserSocialAuth from common.djangoapps.student.models import ( AccountRecovery, @@ -52,7 +50,7 @@ from openedx.features.enterprise_support.tests.factories import EnterpriseCustom def mock_render_to_string(template_name, context): """Return a string that encodes template_name and context""" - return str((template_name, sorted(iteritems(context)))) + return str((template_name, sorted(context.items()))) def mock_render_to_response(template_name): @@ -64,7 +62,7 @@ def mock_render_to_response(template_name): return HttpResponse(template_name) -class CreateAccountMixin(object): # lint-amnesty, pylint: disable=missing-class-docstring +class CreateAccountMixin: # lint-amnesty, pylint: disable=missing-class-docstring def create_account(self, username, password, email): # pylint: disable=missing-docstring registration_url = reverse('user_api_registration') @@ -90,7 +88,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc password = "test" def setUp(self): - super(TestAccountApi, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.request_factory = RequestFactory() self.table = "student_languageproficiency" self.user = UserFactory.create(password=self.password) @@ -174,7 +172,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc def test_set_single_social_link(self): social_links = [ - dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username)) + dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}") ] update_account_settings(self.user, {"social_links": social_links}) account_settings = get_account_settings(self.default_request)[0] @@ -182,8 +180,8 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc def test_set_multiple_social_links(self): social_links = [ - dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username)), - dict(platform="twitter", social_link="https://www.twitter.com/{}".format(self.user.username)), + dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}"), + dict(platform="twitter", social_link=f"https://www.twitter.com/{self.user.username}"), ] update_account_settings(self.user, {"social_links": social_links}) account_settings = get_account_settings(self.default_request)[0] @@ -191,13 +189,13 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc def test_add_social_links(self): original_social_links = [ - dict(platform="facebook", social_link="https://www.facebook.com/{}".format(self.user.username)) + dict(platform="facebook", social_link=f"https://www.facebook.com/{self.user.username}") ] update_account_settings(self.user, {"social_links": original_social_links}) extra_social_links = [ - dict(platform="twitter", social_link="https://www.twitter.com/{}".format(self.user.username)), - dict(platform="linkedin", social_link="https://www.linkedin.com/in/{}".format(self.user.username)), + dict(platform="twitter", social_link=f"https://www.twitter.com/{self.user.username}"), + dict(platform="linkedin", social_link=f"https://www.linkedin.com/in/{self.user.username}"), ] update_account_settings(self.user, {"social_links": extra_social_links}) @@ -229,7 +227,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc def test_unsupported_social_link_platform(self): social_links = [ - dict(platform="unsupported", social_link="https://www.unsupported.com/{}".format(self.user.username)) + dict(platform="unsupported", social_link=f"https://www.unsupported.com/{self.user.username}") ] with pytest.raises(AccountValidationError): update_account_settings(self.user, {"social_links": social_links}) @@ -502,9 +500,9 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase): # pylint: disable=missing-docstring - USERNAME = u'frank-underwood' - PASSWORD = u'ṕáśśẃőŕd' - EMAIL = u'frank+underwood@example.com' + USERNAME = 'frank-underwood' + PASSWORD = 'ṕáśśẃőŕd' + EMAIL = 'frank+underwood@example.com' ID = -1 def test_create_account(self): @@ -529,10 +527,10 @@ class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase): 'email': self.EMAIL, 'id': self.ID, 'name': self.USERNAME, - 'gender': None, 'goals': u'', + 'gender': None, 'goals': '', 'is_active': False, 'level_of_education': None, - 'mailing_address': u'', + 'mailing_address': '', 'year_of_birth': None, 'country': None, 'state': None, @@ -561,11 +559,11 @@ class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase): """ # Set user password to NFKD format so that we can test that it is normalized to # NFKC format upon account creation. - self.create_account(self.USERNAME, unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), self.EMAIL) + self.create_account(self.USERNAME, unicodedata.normalize('NFKD', 'Ṗŕệṿïệẅ Ṯệẍt'), self.EMAIL) user = User.objects.get(username=self.USERNAME) salt_val = user.password.split('$')[1] - expected_user_password = make_password(unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), salt_val) + expected_user_password = make_password(unicodedata.normalize('NFKC', 'Ṗŕệṿïệẅ Ṯệẍt'), salt_val) assert expected_user_password == user.password diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py index 089f063b7e..3608073a52 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py @@ -5,14 +5,13 @@ Tests for helpers.py import datetime import hashlib +from unittest.mock import patch from django.test import TestCase -from mock import patch from pytz import UTC from openedx.core.djangolib.testing.utils import skip_unless_lms from common.djangoapps.student.tests.factories import UserFactory -from six import text_type # lint-amnesty, pylint: disable=wrong-import-order from ..image_helpers import get_profile_image_urls_for_user @@ -28,7 +27,7 @@ class ProfileImageUrlTestCase(TestCase): """ def setUp(self): - super(ProfileImageUrlTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory() # Ensure that parental controls don't apply to this user self.user.profile.year_of_birth = 1980 @@ -46,7 +45,7 @@ class ProfileImageUrlTestCase(TestCase): """ Verify correct url structure for a default profile image. """ - assert actual_url == '/static/default_{size}.png'.format(size=expected_pixels) + assert actual_url == f'/static/default_{expected_pixels}.png' def verify_urls(self, actual_urls, expected_name, is_default=False): """ @@ -68,7 +67,7 @@ class ProfileImageUrlTestCase(TestCase): self.user.profile.profile_image_uploaded_at = TEST_PROFILE_IMAGE_UPLOAD_DT self.user.profile.save() # lint-amnesty, pylint: disable=no-member expected_name = hashlib.md5(( - 'secret' + text_type(self.user.username)).encode('utf-8')).hexdigest() + 'secret' + str(self.user.username)).encode('utf-8')).hexdigest() actual_urls = get_profile_image_urls_for_user(self.user) self.verify_urls(actual_urls, expected_name, is_default=False) diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py b/openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py index fc15cb0088..9697aa50be 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_permissions.py @@ -13,7 +13,7 @@ class CanDeactivateUserTest(TestCase): """ Tests for user deactivation API permissions """ def setUp(self): - super(CanDeactivateUserTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.request = RequestFactory().get('/test/url') def test_api_permission_superuser(self): @@ -46,7 +46,7 @@ class CanRetireUserTest(TestCase): """ Tests for user retirement API permissions """ def setUp(self): - super(CanRetireUserTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.request = RequestFactory().get('/test/url') def test_api_permission_superuser(self): diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py index 7cecac8cbd..ac821988c5 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_retirement_views.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Test cases to cover account retirement views """ @@ -7,11 +6,10 @@ Test cases to cover account retirement views import datetime import json import unittest +from unittest import mock import ddt -import mock import pytz -import six from consent.models import DataSharingConsent from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user @@ -29,8 +27,6 @@ from enterprise.models import ( from integrated_channels.sap_success_factors.models import SapSuccessFactorsLearnerDataTransmissionAudit from opaque_keys.edx.keys import CourseKey from rest_framework import status -from six import iteritems, text_type -from six.moves import range from social_django.models import UserSocialAuth from wiki.models import Article, ArticleRevision from wiki.models.pluginbase import RevisionPlugin, RevisionPluginRevision @@ -109,7 +105,7 @@ class TestAccountDeactivation(TestCase): """ def setUp(self): - super(TestAccountDeactivation, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_user = UserFactory() self.url = reverse('accounts_deactivation', kwargs={'username': self.test_user.username}) @@ -180,7 +176,7 @@ class TestDeactivateLogout(RetirementTestCase): Tests the account deactivation/logout endpoint. """ def setUp(self): - super(TestDeactivateLogout, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_password = 'password' self.test_user = UserFactory(password=self.test_password) UserSocialAuth.objects.create( @@ -277,7 +273,7 @@ class TestPartnerReportingCleanup(ModuleStoreTestCase): """ def setUp(self): - super(TestPartnerReportingCleanup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_superuser = SuperuserFactory() self.course = CourseFactory() self.course_awesome_org = CourseFactory(org='awesome_org') @@ -389,7 +385,7 @@ class TestPartnerReportingPut(RetirementTestCase, ModuleStoreTestCase): """ def setUp(self): - super(TestPartnerReportingPut, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_superuser = SuperuserFactory() self.course = CourseFactory() self.course_awesome_org = CourseFactory(org='awesome_org') @@ -496,7 +492,7 @@ class TestPartnerReportingList(ModuleStoreTestCase): ] def setUp(self): - super(TestPartnerReportingList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_superuser = SuperuserFactory() self.course = CourseFactory() self.course_awesome_org = CourseFactory(org='awesome_org') @@ -698,7 +694,7 @@ class TestAccountRetirementList(RetirementTestCase): """ def setUp(self): - super(TestAccountRetirementList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_superuser = SuperuserFactory() self.headers = build_jwt_headers(self.test_superuser) self.url = reverse('accounts_retirement_queue') @@ -737,7 +733,7 @@ class TestAccountRetirementList(RetirementTestCase): del retirement['created'] del retirement['modified'] - six.assertCountEqual(self, response_data, expected_data) + self.assertCountEqual(response_data, expected_data) def test_empty(self): """ @@ -858,7 +854,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase): """ def setUp(self): - super(TestAccountRetirementsByStatusAndDate, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_superuser = SuperuserFactory() self.headers = build_jwt_headers(self.test_superuser) self.url = reverse('accounts_retirements_by_status_and_date') @@ -910,7 +906,7 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase): except KeyError: pass - six.assertCountEqual(self, response_data, expected_data) + self.assertCountEqual(response_data, expected_data) def test_empty(self): """ @@ -1005,7 +1001,7 @@ class TestAccountRetirementRetrieve(RetirementTestCase): Tests the account retirement retrieval endpoint. """ def setUp(self): - super(TestAccountRetirementRetrieve, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_user = UserFactory() self.test_superuser = SuperuserFactory() self.url = reverse('accounts_retirement_retrieve', kwargs={'username': self.test_user.username}) @@ -1076,7 +1072,7 @@ class TestAccountRetirementCleanup(RetirementTestCase): Tests the account retirement cleanup endpoint. """ def setUp(self): - super(TestAccountRetirementCleanup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.pending_state = RetirementState.objects.get(state_name='PENDING') self.complete_state = RetirementState.objects.get(state_name='COMPLETE') self.retirements = [] @@ -1151,7 +1147,7 @@ class TestAccountRetirementUpdate(RetirementTestCase): Tests the account retirement endpoint. """ def setUp(self): - super(TestAccountRetirementUpdate, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.pending_state = RetirementState.objects.get(state_name='PENDING') self.locking_state = RetirementState.objects.get(state_name='LOCKING_ACCOUNT') @@ -1291,7 +1287,7 @@ class TestAccountRetirementPost(RetirementTestCase): Tests the account retirement endpoint. """ def setUp(self): - super(TestAccountRetirementPost, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_user = UserFactory() self.test_superuser = SuperuserFactory() @@ -1403,7 +1399,7 @@ class TestAccountRetirementPost(RetirementTestCase): with mock.patch(path, side_effect=Exception('Unexpected Exception')) as mock_get_retirement: data = {'username': self.test_user.username} response = self.post_and_assert_status(data, status.HTTP_500_INTERNAL_SERVER_ERROR) - assert 'Unexpected Exception' == text_type(response.json()) + assert 'Unexpected Exception' == str(response.json()) mock_get_retirement.assert_called_once_with(self.original_username) def test_retire_user_where_user_already_retired(self): @@ -1416,7 +1412,7 @@ class TestAccountRetirementPost(RetirementTestCase): def test_retire_user_where_username_not_provided(self): response = self.post_and_assert_status({}, status.HTTP_404_NOT_FOUND) - expected_response_message = {'message': text_type('The user was not specified.')} + expected_response_message = {'message': 'The user was not specified.'} assert expected_response_message == response.json() @mock.patch('openedx.core.djangoapps.user_api.accounts.views.get_profile_image_names') @@ -1434,10 +1430,10 @@ class TestAccountRetirementPost(RetirementTestCase): 'is_active': False, 'username': self.retired_username, } - for field, expected_value in iteritems(expected_user_values): + for field, expected_value in expected_user_values.items(): assert expected_value == getattr(self.test_user, field) - for field, expected_value in iteritems(USER_PROFILE_PII): + for field, expected_value in USER_PROFILE_PII.items(): assert expected_value == getattr(self.test_user.profile, field) assert self.test_user.profile.profile_image_uploaded_at is None @@ -1468,7 +1464,7 @@ class TestAccountRetirementPost(RetirementTestCase): self.post_and_assert_status(data) def test_deletes_pii_from_user_profile(self): - for model_field, value_to_assign in iteritems(USER_PROFILE_PII): + for model_field, value_to_assign in USER_PROFILE_PII.items(): if value_to_assign == '': value = 'foo' else: @@ -1477,7 +1473,7 @@ class TestAccountRetirementPost(RetirementTestCase): AccountRetirementView.clear_pii_from_userprofile(self.test_user) - for model_field, value_to_assign in iteritems(USER_PROFILE_PII): + for model_field, value_to_assign in USER_PROFILE_PII.items(): assert value_to_assign == getattr(self.test_user.profile, model_field) social_links = SocialLink.objects.filter( @@ -1570,7 +1566,7 @@ class TestLMSAccountRetirementPost(RetirementTestCase, ModuleStoreTestCase): Tests the LMS account retirement (GDPR P2) endpoint. """ def setUp(self): - super(TestLMSAccountRetirementPost, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.pii_standin = 'PII here' self.course = CourseFactory() self.test_user = UserFactory() diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_serializers.py b/openedx/core/djangoapps/user_api/accounts/tests/test_serializers.py index 448d5a19be..34b4cb0b0e 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_serializers.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_serializers.py @@ -18,7 +18,7 @@ LOGGER_NAME = "openedx.core.djangoapps.user_api.accounts.serializers" class UserReadOnlySerializerTest(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring def setUp(self): - super(UserReadOnlySerializerTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() request_factory = RequestFactory() self.request = request_factory.get('/api/user/v1/accounts/') self.user = UserFactory.build(username='test_user', email='test_user@test.com') diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_settings_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_settings_views.py index c2c70b13df..3930a777bf 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_settings_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_settings_views.py @@ -1,8 +1,7 @@ """ Tests for views related to account settings. """ -# -*- coding: utf-8 -*- -import mock +from unittest import mock from django.conf import settings from django.contrib import messages from django.contrib.messages.middleware import MessageMiddleware @@ -49,7 +48,7 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, SiteMixin, ProgramsApiCon @mock.patch("django.conf.settings.MESSAGE_STORAGE", 'django.contrib.messages.storage.cookie.CookieStorage') def setUp(self): # pylint: disable=arguments-differ - super(AccountSettingsViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) CommerceConfiguration.objects.create(cache_ttl=10, enabled=True) self.client.login(username=self.USERNAME, password=self.PASSWORD) diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_utils.py b/openedx/core/djangoapps/user_api/accounts/tests/test_utils.py index 3cc319cfbc..f6110be0b7 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_utils.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_utils.py @@ -22,7 +22,7 @@ class UserAccountSettingsTest(TestCase): """Unit tests for setting Social Media Links.""" def setUp(self): # lint-amnesty, pylint: disable=useless-super-delegation - super(UserAccountSettingsTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() def validate_social_link(self, social_platform, link): """ @@ -71,7 +71,7 @@ class CompletionUtilsTestCase(SharedModuleStoreTestCase, CompletionWaffleTestMix """ Creates a test course that can be used for non-destructive tests """ - super(CompletionUtilsTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.override_waffle_switch(True) self.engaged_user = UserFactory.create() self.cruft_user = UserFactory.create() @@ -122,7 +122,7 @@ class CompletionUtilsTestCase(SharedModuleStoreTestCase, CompletionWaffleTestMix self.cruft_user ) assert block_url ==\ - u'test_url:9999/courses/{org}/{course}/{run}/jump_to/i4x://{org}/{course}/vertical/{vertical_id}'.format( + 'test_url:9999/courses/{org}/{course}/{run}/jump_to/i4x://{org}/{course}/vertical/{vertical_id}'.format( org=self.course.location.course_key.org, course=self.course.location.course_key.course, run=self.course.location.course_key.run, diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py index c3e2fa9dd8..bdb0ab45db 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Test cases to cover Accounts-related behaviors of the User API application """ @@ -8,17 +7,15 @@ import datetime import hashlib import json from copy import deepcopy +from unittest import mock import ddt -import mock import pytz -import six from django.conf import settings from django.test.testcases import TransactionTestCase from django.test.utils import override_settings from django.urls import reverse from rest_framework.test import APIClient, APITestCase -from six.moves import range from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user from openedx.core.djangoapps.user_api.accounts import ACCOUNT_VISIBILITY_PREF_KEY @@ -37,8 +34,8 @@ TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo TEST_PROFILE_IMAGE_BACKEND = deepcopy(settings.PROFILE_IMAGE_BACKEND) TEST_PROFILE_IMAGE_BACKEND['options']['base_url'] = '/profile-images/' -TEST_BIO_VALUE = u"Tired mother of twins" -TEST_LANGUAGE_PROFICIENCY_CODE = u"hi" +TEST_BIO_VALUE = "Tired mother of twins" +TEST_LANGUAGE_PROFICIENCY_CODE = "hi" class UserAPITestCase(APITestCase): @@ -47,7 +44,7 @@ class UserAPITestCase(APITestCase): """ def setUp(self): - super(UserAPITestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.anonymous_client = APIClient() self.different_user = UserFactory.create(password=TEST_PASSWORD) @@ -151,7 +148,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase): ENABLED_CACHES = ['default'] def setUp(self): - super(TestOwnUsernameAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.url = reverse("own_username_api") @@ -209,7 +206,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): ENABLED_CACHES = ['default'] def setUp(self): - super(TestAccountsAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.url = reverse("accounts_api", kwargs={'username': self.user.username}) @@ -341,7 +338,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): self.create_mock_profile(self.user) set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, PRIVATE_VISIBILITY) - response = self.send_get(client, query_parameters='email={}'.format(self.user.email)) + response = self.send_get(client, query_parameters=f'email={self.user.email}') self._verify_full_account_response(response) # Note: using getattr so that the patching works even if there is no configuration. @@ -413,7 +410,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): response = self.send_get(client, query_parameters='view=shared') verify_fields_visible_to_all_users(response) - response = self.send_get(client, query_parameters='view=shared&email={}'.format(self.user.email)) + response = self.send_get(client, query_parameters=f'view=shared&email={self.user.email}') verify_fields_visible_to_all_users(response) @ddt.data( @@ -428,7 +425,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, CUSTOM_VISIBILITY) shared_fields = ("bio", "language_proficiencies", "name") for field_name in shared_fields: - set_user_preference(self.user, "visibility.{}".format(field_name), ALL_USERS_VISIBILITY) + set_user_preference(self.user, f"visibility.{field_name}", ALL_USERS_VISIBILITY) # make API request client = self.login_client(api_client, requesting_username) @@ -465,7 +462,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): set_user_preference(self.user, ACCOUNT_VISIBILITY_PREF_KEY, CUSTOM_VISIBILITY) shared_fields = ("bio", "language_proficiencies") for field_name in shared_fields: - set_user_preference(self.user, "visibility.{}".format(field_name), ALL_USERS_VISIBILITY) + set_user_preference(self.user, f"visibility.{field_name}", ALL_USERS_VISIBILITY) # make API request client = self.login_client(api_client, requesting_username) @@ -572,19 +569,19 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): assert 403 == response.status_code @ddt.data( - ("gender", "f", "not a gender", u'"not a gender" is not a valid choice.'), - ("level_of_education", "none", u"ȻħȺɍłɇs", u'"ȻħȺɍłɇs" is not a valid choice.'), - ("country", "GB", "XY", u'"XY" is not a valid choice.'), - ("state", "MA", "PY", u'"PY" is not a valid choice.'), - ("year_of_birth", 2009, "not_an_int", u"A valid integer is required."), - ("name", "bob", "z" * 256, u"Ensure this field has no more than 255 characters."), - ("name", u"ȻħȺɍłɇs", " ", u"The name field must be at least 1 character long."), + ("gender", "f", "not a gender", '"not a gender" is not a valid choice.'), + ("level_of_education", "none", "ȻħȺɍłɇs", '"ȻħȺɍłɇs" is not a valid choice.'), + ("country", "GB", "XY", '"XY" is not a valid choice.'), + ("state", "MA", "PY", '"PY" is not a valid choice.'), + ("year_of_birth", 2009, "not_an_int", "A valid integer is required."), + ("name", "bob", "z" * 256, "Ensure this field has no more than 255 characters."), + ("name", "ȻħȺɍłɇs", " ", "The name field must be at least 1 character long."), ("goals", "Smell the roses"), ("mailing_address", "Sesame Street"), # Note that we store the raw data, so it is up to client to escape the HTML. ( - "bio", u"Lacrosse-playing superhero 壓是進界推日不復女", - "z" * 301, u"The about me field must be at most 300 characters long." + "bio", "Lacrosse-playing superhero 壓是進界推日不復女", + "z" * 301, "The about me field must be at most 300 characters long." ), ("account_privacy", ALL_USERS_VISIBILITY), ("account_privacy", PRIVATE_VISIBILITY), @@ -610,13 +607,13 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): if fails_validation_value: error_response = self.send_patch(client, {field: fails_validation_value}, expected_status=400) - expected_user_message = u'This value is invalid.' + expected_user_message = 'This value is invalid.' if field == 'bio': - expected_user_message = u"The about me field must be at most 300 characters long." + expected_user_message = "The about me field must be at most 300 characters long." assert expected_user_message == error_response.data['field_errors'][field]['user_message'] - assert u"Value '{value}' is not valid for field '{field}': {messages}"\ + assert "Value '{value}' is not valid for field '{field}': {messages}"\ .format(value=fails_validation_value, field=field, messages=[developer_validation_message]) ==\ @@ -648,7 +645,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): Internal helper to check the error messages returned """ assert 'This field is not editable via this API' == data['field_errors'][field_name]['developer_message'] - assert u"The '{0}' field cannot be edited."\ + assert "The '{}' field cannot be edited."\ .format(field_name) == data['field_errors'][field_name]['user_message'] for field_name in ["username", "date_joined", "is_active", "profile_image", "requires_parental_consent"]: @@ -708,7 +705,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): """ assert 3 == len(change_info) assert old_name == change_info[0] - assert u'Name change requested through account API by {}'.format(requester) == change_info[1] + assert f'Name change requested through account API by {requester}' == change_info[1] assert change_info[2] is not None # Verify the new name was also stored. get_response = self.send_get(self.client) @@ -812,28 +809,28 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): # than django model id. for proficiencies in ([{"code": "en"}, {"code": "fr"}, {"code": "es"}], [{"code": "fr"}], [{"code": "aa"}], []): response = self.send_patch(client, {"language_proficiencies": proficiencies}) - six.assertCountEqual(self, response.data["language_proficiencies"], proficiencies) + self.assertCountEqual(response.data["language_proficiencies"], proficiencies) @ddt.data( ( - u"not_a_list", - {u'non_field_errors': [u'Expected a list of items but got type "unicode".']} + "not_a_list", + {'non_field_errors': ['Expected a list of items but got type "unicode".']} ), ( - [u"not_a_JSON_object"], - [{u'non_field_errors': [u'Invalid data. Expected a dictionary, but got unicode.']}] + ["not_a_JSON_object"], + [{'non_field_errors': ['Invalid data. Expected a dictionary, but got unicode.']}] ), ( [{}], - [{'code': [u'This field is required.']}] + [{'code': ['This field is required.']}] ), ( - [{u"code": u"invalid_language_code"}], - [{'code': [u'"invalid_language_code" is not a valid choice.']}] + [{"code": "invalid_language_code"}], + [{'code': ['"invalid_language_code" is not a valid choice.']}] ), ( - [{u"code": u"kw"}, {u"code": u"el"}, {u"code": u"kw"}], - [u'The language_proficiencies field must consist of unique languages.'] + [{"code": "kw"}, {"code": "el"}, {"code": "kw"}], + ['The language_proficiencies field must consist of unique languages.'] ), ) @ddt.unpack @@ -842,8 +839,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): Verify we handle error cases when patching the language_proficiencies field. """ - if six.PY3: - expected_error_message = six.text_type(expected_error_message).replace('unicode', 'str') + expected_error_message = str(expected_error_message).replace('unicode', 'str') client = self.login_client("client", "user") response = self.send_patch(client, {"language_proficiencies": patch_value}, expected_status=400) @@ -925,7 +921,7 @@ class TestAccountAPITransactions(TransactionTestCase): """ def setUp(self): - super(TestAccountAPITransactions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.client = APIClient() self.user = UserFactory.create(password=TEST_PASSWORD) self.url = reverse("accounts_api", kwargs={'username': self.user.username}) @@ -950,7 +946,7 @@ class TestAccountAPITransactions(TransactionTestCase): response = self.client.get(self.url) data = response.data assert old_email == data['email'] - assert u'm' == data['gender'] + assert 'm' == data['gender'] @ddt.ddt @@ -960,7 +956,7 @@ class UsernameReplacementViewTests(APITestCase): SERVICE_USERNAME = 'test_replace_username_service_worker' def setUp(self): - super(UsernameReplacementViewTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.service_user = UserFactory(username=self.SERVICE_USERNAME) self.url = reverse("username_replacement") @@ -969,7 +965,7 @@ class UsernameReplacementViewTests(APITestCase): Helper function for creating headers for the JWT authentication. """ token = create_jwt_for_user(user) - headers = {'HTTP_AUTHORIZATION': u'JWT {}'.format(token)} + headers = {'HTTP_AUTHORIZATION': f'JWT {token}'} return headers def call_api(self, user, data): diff --git a/openedx/core/djangoapps/user_api/accounts/tests/testutils.py b/openedx/core/djangoapps/user_api/accounts/tests/testutils.py index ad04da509f..8befbbdc90 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/testutils.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/testutils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Utility functions, constants, etc. for testing. """ @@ -10,7 +9,7 @@ from common.djangoapps.util.password_policy_validators import DEFAULT_MAX_PASSWO INVALID_NAMES = [ None, '', - u'' + '' ] INVALID_USERNAMES_ASCII = [ @@ -20,39 +19,39 @@ INVALID_USERNAMES_ASCII = [ ] INVALID_USERNAMES_UNICODE = [ - u'invalid-unicode_fŕáńḱ', + 'invalid-unicode_fŕáńḱ', ] INVALID_USERNAMES = [ None, - u'', - u'a', - u'a' * (USERNAME_MAX_LENGTH + 1), + '', + 'a', + 'a' * (USERNAME_MAX_LENGTH + 1), ] + INVALID_USERNAMES_ASCII + INVALID_USERNAMES_UNICODE INVALID_EMAILS = [ None, - u'', - u'a', + '', + 'a', 'no_domain', 'no+domain', '@', '@domain.com', 'test@no_extension', - u'fŕáńḱ@example.com', + 'fŕáńḱ@example.com', # Long email -- subtract the length of the @domain # except for one character (so we exceed the max length limit) - u'{user}@example.com'.format( - user=(u'e' * (EMAIL_MAX_LENGTH - 11)) + '{user}@example.com'.format( + user=('e' * (EMAIL_MAX_LENGTH - 11)) ) ] INVALID_PASSWORDS = [ None, - u'', - u'a', - u'a' * (DEFAULT_MAX_PASSWORD_LENGTH + 1), + '', + 'a', + 'a' * (DEFAULT_MAX_PASSWORD_LENGTH + 1), ] INVALID_COUNTRIES = [ @@ -63,25 +62,25 @@ INVALID_COUNTRIES = [ VALID_NAMES = [ 'Validation Bot', - u'Validation Bot' + 'Validation Bot' ] VALID_USERNAMES_UNICODE = [ - u'Enchanté', - u'username_with_@', - u'username with spaces', - u'eastern_arabic_numbers_١٢٣', + 'Enchanté', + 'username_with_@', + 'username with spaces', + 'eastern_arabic_numbers_١٢٣', ] VALID_USERNAMES = [ - u'username', - u'a' * USERNAME_MIN_LENGTH, - u'a' * USERNAME_MAX_LENGTH, - u'-' * USERNAME_MIN_LENGTH, - u'-' * USERNAME_MAX_LENGTH, - u'_username_', - u'-username-', - u'-_username_-' + 'username', + 'a' * USERNAME_MIN_LENGTH, + 'a' * USERNAME_MAX_LENGTH, + '-' * USERNAME_MIN_LENGTH, + '-' * USERNAME_MAX_LENGTH, + '_username_', + '-username-', + '-_username_-' ] VALID_EMAILS = [ @@ -89,11 +88,11 @@ VALID_EMAILS = [ ] VALID_PASSWORDS = [ - u'good_password_339', + 'good_password_339', ] VALID_COUNTRIES = [ - u'PK', - u'Pakistan', - u'US' + 'PK', + 'Pakistan', + 'US' ] diff --git a/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py b/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py index 5b0ac27226..b8f279c919 100644 --- a/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/course_tag/tests/test_api.py @@ -16,7 +16,7 @@ class TestCourseTagAPI(TestCase): """ def setUp(self): - super(TestCourseTagAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create() self.course_id = CourseLocator('test_org', 'test_course_number', 'test_run') self.test_key = 'test_key' diff --git a/openedx/core/djangoapps/user_api/management/commands/bulk_user_org_email_optout.py b/openedx/core/djangoapps/user_api/management/commands/bulk_user_org_email_optout.py index ca0222ec6b..e465ff5610 100644 --- a/openedx/core/djangoapps/user_api/management/commands/bulk_user_org_email_optout.py +++ b/openedx/core/djangoapps/user_api/management/commands/bulk_user_org_email_optout.py @@ -25,7 +25,6 @@ from textwrap import dedent from django.core.management.base import BaseCommand, CommandError from django.db import connections from django.db.utils import DatabaseError -from six.moves import range log = logging.getLogger(__name__) @@ -89,15 +88,15 @@ class Command(BaseCommand): optout_path = options['optout_csv_path'] if chunk_size <= 0: - raise CommandError(u'Only positive chunk size is allowed ({}).'.format(chunk_size)) + raise CommandError(f'Only positive chunk size is allowed ({chunk_size}).') if sleep_between < 0: - raise CommandError(u'Only non-negative sleep between seconds is allowed ({}).'.format(sleep_between)) + raise CommandError(f'Only non-negative sleep between seconds is allowed ({sleep_between}).') # Read the CSV file. Log the number of user/org rows read. - with open(optout_path, 'r') as csv_file: + with open(optout_path) as csv_file: optout_reader = csv.reader(csv_file) optout_rows = list(optout_reader) - log.info(u"Read %s opt-out rows from CSV file '%s'.", len(optout_rows), optout_path) + log.info("Read %s opt-out rows from CSV file '%s'.", len(optout_rows), optout_path) cursor = connections['default'].cursor() @@ -108,7 +107,7 @@ class Command(BaseCommand): start_idx = curr_row_idx end_idx = min(start_idx + chunk_size - 1, len(optout_rows) - 1) - log.info(u"Attempting opt-out for rows (%s, %s) through (%s, %s)...", + log.info("Attempting opt-out for rows (%s, %s) through (%s, %s)...", optout_rows[start_idx][0], optout_rows[start_idx][1], optout_rows[end_idx][0], optout_rows[end_idx][1]) @@ -131,16 +130,16 @@ class Command(BaseCommand): cursor.execute(query) except DatabaseError as err: cursor.execute('ROLLBACK;') - log.error(u"Rolled-back opt-out for rows (%s, %s) through (%s, %s): %s", + log.error("Rolled-back opt-out for rows (%s, %s) through (%s, %s): %s", optout_rows[start_idx][0], optout_rows[start_idx][1], optout_rows[end_idx][0], optout_rows[end_idx][1], str(err)) raise else: cursor.execute('COMMIT;') - log.info(u"Committed opt-out for rows (%s, %s) through (%s, %s).", + log.info("Committed opt-out for rows (%s, %s) through (%s, %s).", optout_rows[start_idx][0], optout_rows[start_idx][1], optout_rows[end_idx][0], optout_rows[end_idx][1]) - log.info(u"Sleeping %s seconds...", sleep_between) + log.info("Sleeping %s seconds...", sleep_between) time.sleep(sleep_between) curr_row_idx += chunk_size diff --git a/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py b/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py index 3b26f1d781..376bad494d 100644 --- a/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py +++ b/openedx/core/djangoapps/user_api/management/commands/cancel_user_retirement_request.py @@ -37,13 +37,13 @@ class Command(BaseCommand): original_email=email_address ) except UserRetirementStatus.DoesNotExist: - raise CommandError(u"No retirement request with email address '{}' exists.".format(email_address)) # lint-amnesty, pylint: disable=raise-missing-from + raise CommandError(f"No retirement request with email address '{email_address}' exists.") # lint-amnesty, pylint: disable=raise-missing-from # Check if the user has started the retirement process -or- not. if retirement_status.current_state.state_name != 'PENDING': raise CommandError( - u"Retirement requests can only be cancelled for users in the PENDING state." - u" Current request state for '{}': {}".format( + "Retirement requests can only be cancelled for users in the PENDING state." + " Current request state for '{}': {}".format( email_address, retirement_status.current_state.state_name ) @@ -58,4 +58,4 @@ class Command(BaseCommand): # No need to delete the accompanying "permanent" retirement request record - it gets done via Django signal. retirement_status.delete() - print(u"Successfully cancelled retirement request for user with email address '{}'.".format(email_address)) + print(f"Successfully cancelled retirement request for user with email address '{email_address}'.") diff --git a/openedx/core/djangoapps/user_api/management/commands/email_opt_in_list.py b/openedx/core/djangoapps/user_api/management/commands/email_opt_in_list.py index e743455ca1..5ac97f8524 100644 --- a/openedx/core/djangoapps/user_api/management/commands/email_opt_in_list.py +++ b/openedx/core/djangoapps/user_api/management/commands/email_opt_in_list.py @@ -33,9 +33,6 @@ from django.core.management.base import BaseCommand, CommandError from django.db import connections from django.utils import timezone from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=unused-import -import six -from six import text_type -from six.moves import range from xmodule.modulestore.django import modulestore @@ -88,7 +85,7 @@ class Command(BaseCommand): QUERY_INTERVAL = 1000 # Default datetime if the user has not set a preference - DEFAULT_DATETIME_STR = datetime.datetime(year=2014, month=12, day=1).isoformat(str(' ')) + DEFAULT_DATETIME_STR = datetime.datetime(year=2014, month=12, day=1).isoformat(' ') def handle(self, *args, **options): """ @@ -109,7 +106,7 @@ class Command(BaseCommand): org_list = options['org_list'] if os.path.exists(file_path): - raise CommandError("File already exists at '{path}'".format(path=file_path)) + raise CommandError(f"File already exists at '{file_path}'") only_courses = options.get("courses") @@ -120,7 +117,7 @@ class Command(BaseCommand): courses = self._get_courses_for_org(org_list) # Add in organizations from the course keys, to ensure we're including orgs with different capitalizations - org_list = list(set(org_list) | set(course.org for course in courses)) + org_list = list(set(org_list) | {course.org for course in courses}) else: courses = list(set(only_courses.split(","))) @@ -135,7 +132,7 @@ class Command(BaseCommand): # Let the user know what's about to happen LOGGER.info( "Retrieving data for courses: {courses}".format( - courses=", ".join([text_type(course) for course in courses]) + courses=", ".join([str(course) for course in courses]) ) ) @@ -148,7 +145,7 @@ class Command(BaseCommand): self._write_email_opt_in_prefs(file_handle, org_list, course_group) # Remind the user where the output file is - LOGGER.info("Output file: {file_path}".format(file_path=file_path)) + LOGGER.info(f"Output file: {file_path}") def _get_courses_for_org(self, org_aliases): """ @@ -176,7 +173,7 @@ class Command(BaseCommand): start_time = time.time() yield execution_time = time.time() - start_time - LOGGER.info("Execution time: {time} seconds".format(time=execution_time)) + LOGGER.info(f"Execution time: {execution_time} seconds") def _write_email_opt_in_prefs(self, file_handle, org_aliases, courses): """ @@ -254,19 +251,19 @@ class Command(BaseCommand): # Only encode to utf-8 in python2 because python3's csv writer can handle unicode. writer.writerow({ "user_id": user_id, - "username": username.encode('utf-8') if six.PY2 else username, - "email": email.encode('utf-8') if six.PY2 else email, + "username": username, + "email": email, # There should not be a case where users are without full_names. We only need this safe check because # of ECOM-1995. - "full_name": full_name.encode('utf-8') if six.PY2 else full_name, - "course_id": course_id.encode('utf-8') if six.PY2 else course_id, + "full_name": full_name, + "course_id": course_id, "is_opted_in_for_email": is_opted_in if is_opted_in else "True", "preference_set_datetime": pref_set_datetime, }) row_count += 1 # Log the number of rows we processed - LOGGER.info("Retrieved {num_rows} records for orgs {org}.".format(num_rows=row_count, org=org_aliases)) + LOGGER.info(f"Retrieved {row_count} records for orgs {org_aliases}.") def _iterate_results(self, cursor): """ @@ -282,14 +279,13 @@ class Command(BaseCommand): rows = cursor.fetchmany(self.QUERY_INTERVAL) if not rows: break - for row in rows: - yield row + yield from rows def _sql_list(self, values): """ Serialize a list of values for including in a SQL "IN" statement. """ - return ",".join(['"{}"'.format(val) for val in values]) + return ",".join([f'"{val}"' for val in values]) def _db_cursor(self): """ diff --git a/openedx/core/djangoapps/user_api/management/commands/migrate_user_profile_langs.py b/openedx/core/djangoapps/user_api/management/commands/migrate_user_profile_langs.py index e25617afbd..dfe27759e8 100644 --- a/openedx/core/djangoapps/user_api/management/commands/migrate_user_profile_langs.py +++ b/openedx/core/djangoapps/user_api/management/commands/migrate_user_profile_langs.py @@ -62,14 +62,14 @@ class Command(BaseCommand): langs += dark_lang_config.beta_languages_list if dark_lang_config.enable_beta_languages else [] if new_lang_code not in langs: - raise CommandError(u'{} is not a configured language code in settings.LANGUAGES ' + raise CommandError('{} is not a configured language code in settings.LANGUAGES ' 'or the current DarkLangConfig.'.format(new_lang_code)) max_id = UserPreference.objects.all().aggregate(Max('id'))['id__max'] - print(u'Updating user language preferences from {} to {}. ' - u'Start id is {}, current max id is {}. ' - u'Chunk size is of {}'.format(old_lang_code, new_lang_code, start, max_id, chunk_size)) + print('Updating user language preferences from {} to {}. ' + 'Start id is {}, current max id is {}. ' + 'Chunk size is of {}'.format(old_lang_code, new_lang_code, start, max_id, chunk_size)) updated_count = 0 @@ -89,7 +89,7 @@ class Command(BaseCommand): updated_count += curr - print(u'Updated rows {} to {}, {} rows affected'.format(start, end - 1, curr)) + print('Updated rows {} to {}, {} rows affected'.format(start, end - 1, curr)) if end >= max_id: break @@ -98,7 +98,7 @@ class Command(BaseCommand): end += chunk_size sleep(sleep_time_secs) - print(u'Finished! Updated {} total preferences from {} to {}'.format( + print('Finished! Updated {} total preferences from {} to {}'.format( updated_count, old_lang_code, new_lang_code diff --git a/openedx/core/djangoapps/user_api/management/commands/populate_retirement_states.py b/openedx/core/djangoapps/user_api/management/commands/populate_retirement_states.py index 0e6f575a0f..5ab54a781b 100644 --- a/openedx/core/djangoapps/user_api/management/commands/populate_retirement_states.py +++ b/openedx/core/djangoapps/user_api/management/commands/populate_retirement_states.py @@ -50,17 +50,17 @@ class Command(BaseCommand): raise CommandError('settings.RETIREMENT_STATES does not exist or is empty.') if not set(REQUIRED_STATES).issubset(set(new_states)): - raise CommandError(u'settings.RETIREMENT_STATES ({}) does not contain all required states ' - u'({})'.format(new_states, REQ_STR)) + raise CommandError('settings.RETIREMENT_STATES ({}) does not contain all required states ' + '({})'.format(new_states, REQ_STR)) # Confirm that the start and end states are in the right places if new_states.index(START_STATE) != 0: - raise CommandError(u'{} must be the first state'.format(START_STATE)) + raise CommandError(f'{START_STATE} must be the first state') num_end_states = len(END_STATES) if new_states[-num_end_states:] != END_STATES: - raise CommandError(u'The last {} states must be these (in this order): ' + raise CommandError('The last {} states must be these (in this order): ' '{}'.format(num_end_states, END_STATES)) def _check_current_users(self): @@ -75,8 +75,8 @@ class Command(BaseCommand): def _check_users_in_states_to_delete(self, states_to_delete): if UserRetirementStatus.objects.filter(current_state__state_name__in=states_to_delete).exists(): - raise CommandError(u'Users exist in a state that is marked for deletion! States to delete' - u'are: {}'.format(states_to_delete)) + raise CommandError('Users exist in a state that is marked for deletion! States to delete' + 'are: {}'.format(states_to_delete)) def _delete_old_states_and_create_new(self, new_states, dry_run=False): """ @@ -151,9 +151,9 @@ class Command(BaseCommand): # Report print("States have been synchronized. Differences:") - print(u" Added: {}".format(created)) - print(u" Removed: {}".format(deleted)) - print(u" Remaining: {}".format(existed)) + print(f" Added: {created}") + print(f" Removed: {deleted}") + print(f" Remaining: {existed}") print("States updated successfully. Current states:") for state in RetirementState.objects.all(): diff --git a/openedx/core/djangoapps/user_api/management/commands/retire_user.py b/openedx/core/djangoapps/user_api/management/commands/retire_user.py index e56f0b35b9..cac9f045e8 100644 --- a/openedx/core/djangoapps/user_api/management/commands/retire_user.py +++ b/openedx/core/djangoapps/user_api/management/commands/retire_user.py @@ -85,15 +85,15 @@ class Command(BaseCommand): retire_dot_oauth2_models(user) AccountRecovery.retire_recovery_email(user.id) except KeyError: - error_message = 'Username not specified {}'.format(user) + error_message = f'Username not specified {user}' logger.error(error_message) raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from except user_model.DoesNotExist: - error_message = 'The user "{}" does not exist.'.format(user.username) + error_message = f'The user "{user.username}" does not exist.' logger.error(error_message) raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from except Exception as exc: # pylint: disable=broad-except - error_message = '500 error deactivating account {}'.format(exc) + error_message = f'500 error deactivating account {exc}' logger.error(error_message) raise CommandError(error_message) # lint-amnesty, pylint: disable=raise-missing-from diff --git a/openedx/core/djangoapps/user_api/management/commands/sync_hubspot_contacts.py b/openedx/core/djangoapps/user_api/management/commands/sync_hubspot_contacts.py index 3907d4fa8f..c11ffb4afe 100644 --- a/openedx/core/djangoapps/user_api/management/commands/sync_hubspot_contacts.py +++ b/openedx/core/djangoapps/user_api/management/commands/sync_hubspot_contacts.py @@ -11,7 +11,7 @@ import traceback from datetime import datetime, timedelta from textwrap import dedent -import six.moves.urllib.parse # pylint: disable=import-error +import urllib.parse # pylint: disable=import-error from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.management.base import BaseCommand, CommandError from edx_rest_api_client.client import EdxRestApiClient @@ -49,7 +49,7 @@ class Command(BaseCommand): """ start_date = datetime.now().date() - timedelta(initial_days) end_date = datetime.now().date() - timedelta(1) - self.stdout.write(u'Getting users from {start} to {end}'.format(start=start_date, end=end_date)) + self.stdout.write(f'Getting users from {start_date} to {end_date}') users_qs = User.objects.filter( date_joined__date__gte=start_date, date_joined__date__lte=end_date @@ -68,7 +68,7 @@ class Command(BaseCommand): """ self.stdout.write( - u'Fetching Users for site {site} from {start} to {end}'.format( + 'Fetching Users for site {site} from {start} to {end}'.format( site=site_domain, start=offset, end=offset + users_query_batch_size ) ) @@ -77,7 +77,7 @@ class Command(BaseCommand): user for user in users if UserAttribute.get_user_attribute(user, 'created_on_site') == site_domain ] - self.stdout.write(u'\tSite Users={count}'.format(count=len(site_users))) + self.stdout.write('\tSite Users={count}'.format(count=len(site_users))) return site_users @@ -88,15 +88,15 @@ class Command(BaseCommand): contacts = [] for user in users_batch: if not hasattr(user, 'profile'): - self.stdout.write(u'skipping user {} due to no profile found'.format(user)) + self.stdout.write(f'skipping user {user} due to no profile found') continue if not user.profile.meta: - self.stdout.write(u'skipping user {} due to no profile meta found'.format(user)) + self.stdout.write(f'skipping user {user} due to no profile meta found') continue try: meta = json.loads(user.profile.meta) except ValueError: - self.stdout.write(u'skipping user {} due to invalid profile meta found'.format(user)) + self.stdout.write(f'skipping user {user} due to invalid profile meta found') continue contact = { @@ -139,12 +139,12 @@ class Command(BaseCommand): contacts.append(contact) api_key = site_conf.get_value('HUBSPOT_API_KEY') - client = EdxRestApiClient(six.moves.urllib.parse.urljoin(HUBSPOT_API_BASE_URL, 'contacts/v1/contact')) + client = EdxRestApiClient(urllib.parse.urljoin(HUBSPOT_API_BASE_URL, 'contacts/v1/contact')) try: client.batch.post(contacts, hapikey=api_key) return len(contacts) except (HttpClientError, HttpServerError) as ex: - message = u'An error occurred while syncing batch of contacts for site {domain}, {message}'.format( + message = 'An error occurred while syncing batch of contacts for site {domain}, {message}'.format( domain=site_conf.site.domain, message=ex.message # lint-amnesty, pylint: disable=no-member ) self.stderr.write(message) @@ -155,7 +155,7 @@ class Command(BaseCommand): Syncs a single site """ site_domain = site_conf.site.domain - self.stdout.write(u'Syncing process started for site {site}'.format(site=site_domain)) + self.stdout.write(f'Syncing process started for site {site_domain}') offset = 0 users_queue = [] @@ -165,7 +165,7 @@ class Command(BaseCommand): while offset < users_count: is_last_iteration = (offset + users_query_batch_size) >= users_count self.stdout.write( - u'Syncing users batch from {start} to {end} for site {site}'.format( + 'Syncing users batch from {start} to {end} for site {site}'.format( start=offset, end=offset + users_query_batch_size, site=site_domain ) ) @@ -177,14 +177,14 @@ class Command(BaseCommand): successfully_synced_contacts += self._sync_with_hubspot(users_batch, site_conf) time.sleep(0.1) # to make sure request per second could not exceed by 10 self.stdout.write( - u'Successfully synced users batch from {start} to {end} for site {site}'.format( + 'Successfully synced users batch from {start} to {end} for site {site}'.format( start=offset, end=offset + users_query_batch_size, site=site_domain ) ) offset += users_query_batch_size self.stdout.write( - u'{count} contacts found and sycned for site {site}'.format( + '{count} contacts found and sycned for site {site}'.format( count=successfully_synced_contacts, site=site_domain ) ) @@ -215,15 +215,15 @@ class Command(BaseCommand): initial_sync_days = options['initial_sync_days'] batch_size = options['batch_size'] try: - self.stdout.write(u'Command execution started with options = {}.'.format(options)) + self.stdout.write(f'Command execution started with options = {options}.') hubspot_sites = self._get_hubspot_enabled_sites() - self.stdout.write(u'{count} hubspot enabled sites found.'.format(count=len(hubspot_sites))) + self.stdout.write('{count} hubspot enabled sites found.'.format(count=len(hubspot_sites))) users_queryset = self._get_users_queryset(initial_sync_days) users_count = users_queryset.count() - self.stdout.write(u'Users count={count}'.format(count=users_count)) + self.stdout.write(f'Users count={users_count}') for site_conf in hubspot_sites: self._sync_site(site_conf, users_queryset, users_count, batch_size) except Exception as ex: traceback.print_exc() - raise CommandError(u'Command failed with traceback %s' % str(ex)) # lint-amnesty, pylint: disable=raise-missing-from + raise CommandError('Command failed with traceback %s' % str(ex)) # lint-amnesty, pylint: disable=raise-missing-from diff --git a/openedx/core/djangoapps/user_api/management/tests/test_bulk_user_org_email_optout.py b/openedx/core/djangoapps/user_api/management/tests/test_bulk_user_org_email_optout.py index 1b05fc1f06..dccee9b083 100644 --- a/openedx/core/djangoapps/user_api/management/tests/test_bulk_user_org_email_optout.py +++ b/openedx/core/djangoapps/user_api/management/tests/test_bulk_user_org_email_optout.py @@ -8,14 +8,14 @@ import os import tempfile from contextlib import contextmanager -import mock +from unittest import mock import pytest from django.core.management import call_command pytestmark = pytest.mark.django_db -CSV_DATA = u"""1,UniversityX +CSV_DATA = """1,UniversityX 2,CollegeX 3,StateUX """ @@ -27,7 +27,7 @@ def _create_test_csv(csv_data): Context manager to create and populate a CSV file - and delete it after usage. """ __, file_name = tempfile.mkstemp(text=True) - with io.open(file_name, 'w') as file_pointer: + with open(file_name, 'w') as file_pointer: file_pointer.write(csv_data) try: yield file_name @@ -41,12 +41,12 @@ def test_successful_dry_run(mock_logger): Run the command with default states for a successful initial population """ with _create_test_csv(CSV_DATA) as tmp_csv_file: - args = ['--dry_run', '--optout_csv_path={}'.format(tmp_csv_file)] + args = ['--dry_run', f'--optout_csv_path={tmp_csv_file}'] call_command('bulk_user_org_email_optout', *args) assert mock_logger.call_count == 3 - mock_logger.assert_any_call(u"Read %s opt-out rows from CSV file '%s'.", 3, tmp_csv_file) + mock_logger.assert_any_call("Read %s opt-out rows from CSV file '%s'.", 3, tmp_csv_file) mock_logger.assert_any_call( - u'Attempting opt-out for rows (%s, %s) through (%s, %s)...', '1', 'UniversityX', '3', 'StateUX' + 'Attempting opt-out for rows (%s, %s) through (%s, %s)...', '1', 'UniversityX', '3', 'StateUX' ) mock_logger.assert_any_call( 'INSERT INTO user_api_userorgtag (`user_id`, `org`, `key`, `value`, `created`, `modified`) \ diff --git a/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py b/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py index 316226ce7e..e167fb5a46 100644 --- a/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py +++ b/openedx/core/djangoapps/user_api/management/tests/test_email_opt_in_list.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """Tests for the email opt-in list management command. """ @@ -9,12 +8,9 @@ import tempfile from collections import defaultdict import ddt -import six from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.management import call_command from django.core.management.base import CommandError -from six import text_type -from six.moves import range from openedx.core.djangoapps.user_api.management.commands import email_opt_in_list from openedx.core.djangoapps.user_api.models import UserOrgTag @@ -31,10 +27,10 @@ from xmodule.modulestore.tests.factories import CourseFactory class EmailOptInListTest(ModuleStoreTestCase): """Tests for the email opt-in list management command. """ USER_USERNAME = "test_user" - USER_FIRST_NAME = u"Ṫëṡẗ" - USER_LAST_NAME = u"Űśéŕ" + USER_FIRST_NAME = "Ṫëṡẗ" + USER_LAST_NAME = "Űśéŕ" - TEST_ORG = u"téśt_őŕǵ" + TEST_ORG = "téśt_őŕǵ" OUTPUT_FILE_NAME = "test_org_email_opt_in.csv" OUTPUT_FIELD_NAMES = [ @@ -50,7 +46,7 @@ class EmailOptInListTest(ModuleStoreTestCase): DEFAULT_DATETIME_STR = "2014-12-01 00:00:00" def setUp(self): - super(EmailOptInListTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create( username=self.USER_USERNAME, @@ -217,14 +213,10 @@ class EmailOptInListTest(ModuleStoreTestCase): course_ids = [] for row in output: course_id = row['course_id'].strip() - # Python3 takes care of the decoding in the csv object - # but python 2 doesn't - if six.PY2: - course_id = course_id.decode('utf-8') course_ids.append(course_id) for course in self.courses: - assert text_type(course.id) in course_ids + assert str(course.id) in course_ids # Choose numbers before and after the query interval boundary @ddt.data(2, 3, 4, 5, 6, 7, 8, 9) @@ -270,11 +262,7 @@ class EmailOptInListTest(ModuleStoreTestCase): @ddt.data(0, 1) def test_not_enough_args(self, num_args): args = ["dummy"] * num_args - if six.PY2: - expected_msg_regex = ( - "^Error: too few arguments$" - ) - elif num_args == 1: + if num_args == 1: expected_msg_regex = ( "^Error: the following arguments are required: ORG_ALIASES$" ) @@ -395,7 +383,7 @@ class EmailOptInListTest(ModuleStoreTestCase): output_path = os.path.join(temp_dir_path, self.OUTPUT_FILE_NAME) org_list = [org] + other_names if only_courses is not None: - only_courses = ",".join(text_type(course_id) for course_id in only_courses) + only_courses = ",".join(str(course_id) for course_id in only_courses) command = email_opt_in_list.Command() @@ -414,8 +402,8 @@ class EmailOptInListTest(ModuleStoreTestCase): with open(output_path) as output_file: reader = csv.DictReader(output_file, fieldnames=self.OUTPUT_FIELD_NAMES) rows = [row for row in reader] # lint-amnesty, pylint: disable=unnecessary-comprehension - except IOError: - self.fail(u"Could not find or open output file at '{path}'".format(path=output_path)) + except OSError: + self.fail(f"Could not find or open output file at '{output_path}'") # Return the output as a list of dictionaries return rows @@ -448,8 +436,8 @@ class EmailOptInListTest(ModuleStoreTestCase): for user, course_id, opt_in_pref in args: assert {'user_id': str(user.id), 'username': user.username, 'email': user.email, 'full_name': (user.profile.name if hasattr(user, 'profile') else ''), - 'course_id': text_type(course_id), - 'is_opted_in_for_email': text_type(opt_in_pref), + 'course_id': str(course_id), + 'is_opted_in_for_email': str(opt_in_pref), 'preference_set_datetime': (self._latest_pref_set_datetime(self.user) if kwargs.get('expect_pref_datetime', True) else self.DEFAULT_DATETIME_STR)} in output[1:] diff --git a/openedx/core/djangoapps/user_api/management/tests/test_populate_retirement_states.py b/openedx/core/djangoapps/user_api/management/tests/test_populate_retirement_states.py index 0944ec8234..e43ebd54f9 100644 --- a/openedx/core/djangoapps/user_api/management/tests/test_populate_retirement_states.py +++ b/openedx/core/djangoapps/user_api/management/tests/test_populate_retirement_states.py @@ -87,7 +87,7 @@ def test_out_of_order_start_state(settings): del settings.RETIREMENT_STATES[0] settings.RETIREMENT_STATES.insert(4, 'PENDING') - with pytest.raises(CommandError, match=u'{} must be the first state'.format(START_STATE)): + with pytest.raises(CommandError, match=f'{START_STATE} must be the first state'): call_command('populate_retirement_states') diff --git a/openedx/core/djangoapps/user_api/management/tests/test_sync_hubspot_contacts.py b/openedx/core/djangoapps/user_api/management/tests/test_sync_hubspot_contacts.py index 8fd6aac72a..4956554b1e 100644 --- a/openedx/core/djangoapps/user_api/management/tests/test_sync_hubspot_contacts.py +++ b/openedx/core/djangoapps/user_api/management/tests/test_sync_hubspot_contacts.py @@ -5,13 +5,12 @@ Test the sync_hubspot_contacts management command import json from datetime import timedelta +from io import StringIO +from unittest.mock import patch from django.core.management import call_command from django.test import TestCase from django.utils import timezone -from django.utils.six import StringIO -from mock import patch -from six.moves import range from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory from openedx.core.djangoapps.user_api.management.commands.sync_hubspot_contacts import Command as sync_command @@ -28,7 +27,7 @@ class TestHubspotSyncCommand(TestCase): @classmethod def setUpClass(cls): - super(TestHubspotSyncCommand, cls).setUpClass() + super().setUpClass() cls.site_config = SiteConfigurationFactory() cls.hubspot_site_config = SiteConfigurationFactory.create( site_values={'HUBSPOT_API_KEY': 'test_key'} @@ -42,11 +41,11 @@ class TestHubspotSyncCommand(TestCase): # Create some test users for i in range(1, 20): profile_meta = { - "first_name": "First Name{0}".format(i), - "last_name": "Last Name{0}".format(i), - "company": "Company{0}".format(i), - "title": "Title{0}".format(i), - "state": "State{0}".format(i), + "first_name": f"First Name{i}", + "last_name": f"Last Name{i}", + "company": f"Company{i}", + "title": f"Title{i}", + "state": f"State{i}", "country": "US", } loe = UserProfile.LEVEL_OF_EDUCATION_CHOICES[0][0] diff --git a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py index 6a80b50e80..18b9d5903e 100644 --- a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py @@ -1,17 +1,16 @@ -# -*- coding: utf-8 -*- """ Unit tests for preference APIs. """ import datetime +from unittest.mock import patch import pytest import ddt from dateutil.parser import parse as parse_datetime from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.test.utils import override_settings from django.urls import reverse -from mock import patch from pytz import common_timezones, utc from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms @@ -50,7 +49,7 @@ class TestPreferenceAPI(CacheIsolationTestCase): password = "test" def setUp(self): - super(TestPreferenceAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.user = UserFactory.create(password=self.password) self.different_user = UserFactory.create(password=self.password) self.staff_user = UserFactory.create(is_staff=True, password=self.password) @@ -107,8 +106,8 @@ class TestPreferenceAPI(CacheIsolationTestCase): """ Verifies the basic behavior of set_user_preference. """ - test_key = u'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ' - test_value = u'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd' + test_key = 'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ' + test_value = 'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd' set_user_preference(self.user, test_key, test_value) assert get_user_preference(self.user, test_key) == test_value set_user_preference(self.user, test_key, "new_value", username=self.user.username) @@ -151,11 +150,11 @@ class TestPreferenceAPI(CacheIsolationTestCase): user_preference_save.side_effect = [Exception, None] with pytest.raises(PreferenceUpdateError) as context_manager: - set_user_preference(self.user, u"new_key_ȻħȺɍłɇs", u"new_value_ȻħȺɍłɇs") + set_user_preference(self.user, "new_key_ȻħȺɍłɇs", "new_value_ȻħȺɍłɇs") assert context_manager.value.developer_message ==\ - u"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs': " + "Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs': " assert context_manager.value.user_message ==\ - u"Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs'." + "Save failed for user preference 'new_key_ȻħȺɍłɇs' with value 'new_value_ȻħȺɍłɇs'." def test_update_user_preferences(self): """ @@ -231,15 +230,15 @@ class TestPreferenceAPI(CacheIsolationTestCase): with pytest.raises(PreferenceUpdateError) as context_manager: update_user_preferences(self.user, {self.test_preference_key: "new_value"}) assert context_manager.value.developer_message ==\ - u"Save failed for user preference 'test_key' with value 'new_value': " + "Save failed for user preference 'test_key' with value 'new_value': " assert context_manager.value.user_message ==\ - u"Save failed for user preference 'test_key' with value 'new_value'." + "Save failed for user preference 'test_key' with value 'new_value'." user_preference_delete.side_effect = [Exception, None] with pytest.raises(PreferenceUpdateError) as context_manager: update_user_preferences(self.user, {self.test_preference_key: None}) - assert context_manager.value.developer_message == u"Delete failed for user preference 'test_key': " - assert context_manager.value.user_message == u"Delete failed for user preference 'test_key'." + assert context_manager.value.developer_message == "Delete failed for user preference 'test_key': " + assert context_manager.value.user_message == "Delete failed for user preference 'test_key'." def test_delete_user_preference(self): """ @@ -270,8 +269,8 @@ class TestPreferenceAPI(CacheIsolationTestCase): user_preference_delete.side_effect = [Exception, None] with pytest.raises(PreferenceUpdateError) as context_manager: delete_user_preference(self.user, self.test_preference_key) - assert context_manager.value.developer_message == u"Delete failed for user preference 'test_key': " - assert context_manager.value.user_message == u"Delete failed for user preference 'test_key'." + assert context_manager.value.developer_message == "Delete failed for user preference 'test_key': " + assert context_manager.value.user_message == "Delete failed for user preference 'test_key'." @ddt.ddt @@ -279,9 +278,9 @@ class UpdateEmailOptInTests(ModuleStoreTestCase): """ Test cases to cover API-driven email list opt-in update workflows """ - USERNAME = u'claire-underwood' - PASSWORD = u'ṕáśśẃőŕd' - EMAIL = u'claire+underwood@example.com' + USERNAME = 'claire-underwood' + PASSWORD = 'ṕáśśẃőŕd' + EMAIL = 'claire+underwood@example.com' def _create_account(self, username, password, email): # pylint: disable=missing-docstring @@ -297,19 +296,19 @@ class UpdateEmailOptInTests(ModuleStoreTestCase): @ddt.data( # Check that a 27 year old can opt-in - (27, True, u"True"), + (27, True, "True"), # Check that a 32-year old can opt-out - (32, False, u"False"), + (32, False, "False"), # Check that someone 14 years old can opt-in - (14, True, u"True"), + (14, True, "True"), # Check that someone 13 years old cannot opt-in (must have turned 13 before this year) - (13, True, u"False"), + (13, True, "False"), # Check that someone 12 years old cannot opt-in - (12, True, u"False") + (12, True, "False") ) @ddt.unpack @override_settings(EMAIL_OPTIN_MINIMUM_AGE=13) @@ -339,7 +338,7 @@ class UpdateEmailOptInTests(ModuleStoreTestCase): update_email_opt_in(user, course.id.org, True) result_obj = UserOrgTag.objects.get(user=user, org=course.id.org, key='email-optin') - assert result_obj.value == u'True' + assert result_obj.value == 'True' def test_update_email_optin_anonymous_user(self): """Verify that the API raises an exception for a user with no profile.""" @@ -350,16 +349,16 @@ class UpdateEmailOptInTests(ModuleStoreTestCase): @ddt.data( # Check that a 27 year old can opt-in, then out. - (27, True, False, u"False"), + (27, True, False, "False"), # Check that a 32-year old can opt-out, then in. - (32, False, True, u"True"), + (32, False, True, "True"), # Check that someone 13 years old can opt-in, then out. - (13, True, False, u"False"), + (13, True, False, "False"), # Check that someone 12 years old cannot opt-in, then explicitly out. - (12, True, False, u"False") + (12, True, False, "False") ) @ddt.unpack @override_settings(EMAIL_OPTIN_MINIMUM_AGE=13) @@ -425,11 +424,11 @@ def get_expected_validation_developer_message(preference_key, preference_value): """ Returns the expected dict of validation messages for the specified key. """ - return u"Value '{preference_value}' not valid for preference '{preference_key}': {error}".format( + return "Value '{preference_value}' not valid for preference '{preference_key}': {error}".format( preference_key=preference_key, preference_value=preference_value, error={ - "key": [u"Ensure this field has no more than 255 characters."] + "key": ["Ensure this field has no more than 255 characters."] } ) @@ -438,11 +437,11 @@ def get_expected_key_error_user_message(preference_key, preference_value): # li """ Returns the expected user message for an invalid key. """ - return u"Invalid user preference key '{preference_key}'.".format(preference_key=preference_key) + return f"Invalid user preference key '{preference_key}'." def get_empty_preference_message(preference_key): """ Returns the validation message shown for an empty preference. """ - return u"Preference '{preference_key}' cannot be set to an empty value.".format(preference_key=preference_key) + return f"Preference '{preference_key}' cannot be set to an empty value." diff --git a/openedx/core/djangoapps/user_api/preferences/tests/test_views.py b/openedx/core/djangoapps/user_api/preferences/tests/test_views.py index 43e1b9bbe8..361c449103 100644 --- a/openedx/core/djangoapps/user_api/preferences/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/preferences/tests/test_views.py @@ -1,16 +1,14 @@ -# -*- coding: utf-8 -*- """ Unit tests for preference APIs. """ import json +from unittest.mock import patch import ddt -import six from django.test.testcases import TransactionTestCase from django.urls import reverse -from mock import patch from rest_framework.test import APIClient from openedx.core.djangolib.testing.utils import skip_unless_lms @@ -20,7 +18,7 @@ from ...accounts.tests.test_views import UserAPITestCase from ..api import set_user_preference from .test_api import get_expected_key_error_user_message, get_expected_validation_developer_message -TOO_LONG_PREFERENCE_KEY = u"x" * 256 +TOO_LONG_PREFERENCE_KEY = "x" * 256 @ddt.ddt @@ -30,7 +28,7 @@ class TestPreferencesAPI(UserAPITestCase): Unit tests /api/user/v1/accounts/{username}/ """ def setUp(self): - super(TestPreferencesAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.url_endpoint_name = "preferences_api" self.url = reverse(self.url_endpoint_name, kwargs={'username': self.user.username}) @@ -151,11 +149,8 @@ class TestPreferencesAPI(UserAPITestCase): expected_status=204 ) response = self.send_get(self.client) - if six.PY2: - pref_dict = {u"dict_pref": u"{u'int_key': 10}", u"string_pref": u"value"} - else: - # lint-amnesty, pylint: disable=bad-option-value, unicode-format-string - pref_dict = {"dict_pref": "{'int_key': 10}", "string_pref": "value"} + # lint-amnesty, pylint: disable=bad-option-value, unicode-format-string + pref_dict = {"dict_pref": "{'int_key': 10}", "string_pref": "value"} assert pref_dict == response.data @ddt.data( @@ -228,22 +223,22 @@ class TestPreferencesAPI(UserAPITestCase): "string_pref": "updated_value", TOO_LONG_PREFERENCE_KEY: "new_value", "new_pref": "new_value", - u"empty_pref_ȻħȺɍłɇs": "", + "empty_pref_ȻħȺɍłɇs": "", "time_zone": "Asia/Africa", }, expected_status=400 ) assert response.data.get('field_errors', None) field_errors = response.data["field_errors"] - assert field_errors == {TOO_LONG_PREFERENCE_KEY: {'developer_message': get_expected_validation_developer_message(TOO_LONG_PREFERENCE_KEY, 'new_value'), 'user_message': get_expected_key_error_user_message(TOO_LONG_PREFERENCE_KEY, 'new_value')}, u'empty_pref_ȻħȺɍłɇs': {'developer_message': u"Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value.", 'user_message': u"Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value."}, 'time_zone': {'developer_message': u"Value 'Asia/Africa' not valid for preference 'time_zone': Not in timezone set.", 'user_message': u"Value 'Asia/Africa' is not a valid time zone selection."}} # pylint: disable=line-too-long + assert field_errors == {TOO_LONG_PREFERENCE_KEY: {'developer_message': get_expected_validation_developer_message(TOO_LONG_PREFERENCE_KEY, 'new_value'), 'user_message': get_expected_key_error_user_message(TOO_LONG_PREFERENCE_KEY, 'new_value')}, 'empty_pref_ȻħȺɍłɇs': {'developer_message': "Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value.", 'user_message': "Preference 'empty_pref_ȻħȺɍłɇs' cannot be set to an empty value."}, 'time_zone': {'developer_message': "Value 'Asia/Africa' not valid for preference 'time_zone': Not in timezone set.", 'user_message': "Value 'Asia/Africa' is not a valid time zone selection."}} # pylint: disable=line-too-long # Verify that GET returns the original preferences response = self.send_get(self.client) expected_preferences = { - "dict_pref": u"{'int_key': 10}", - "string_pref": u"value", - "extra_pref": u"extra_value", - "time_zone": u"Pacific/Midway", + "dict_pref": "{'int_key': 10}", + "string_pref": "value", + "extra_pref": "extra_value", + "time_zone": "Pacific/Midway", } assert expected_preferences == response.data @@ -256,14 +251,14 @@ class TestPreferencesAPI(UserAPITestCase): # Verify a non-dict request response = self.send_patch(self.client, "non_dict_request", expected_status=400) assert response.data ==\ - {'developer_message': u'No data provided for user preference update', - 'user_message': u'No data provided for user preference update'} + {'developer_message': 'No data provided for user preference update', + 'user_message': 'No data provided for user preference update'} # Verify an empty dict request response = self.send_patch(self.client, {}, expected_status=400) assert response.data ==\ - {'developer_message': u'No data provided for user preference update', - 'user_message': u'No data provided for user preference update'} + {'developer_message': 'No data provided for user preference update', + 'user_message': 'No data provided for user preference update'} @ddt.data( ("different_client", "different_user"), @@ -300,7 +295,7 @@ class TestPreferencesAPITransactions(TransactionTestCase): test_password = "test" def setUp(self): - super(TestPreferencesAPITransactions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.client = APIClient() self.user = UserFactory.create(password=TEST_PASSWORD) self.url = reverse("preferences_api", kwargs={'username': self.user.username}) @@ -345,7 +340,7 @@ class TestPreferencesDetailAPI(UserAPITestCase): Unit tests /api/user/v1/accounts/{username}/{preference_key} """ def setUp(self): - super(TestPreferencesDetailAPI, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.test_pref_key = "test_key" self.test_pref_value = "test_value" set_user_preference(self.user, self.test_pref_key, self.test_pref_value) @@ -428,7 +423,7 @@ class TestPreferencesDetailAPI(UserAPITestCase): set_user_preference(self.user, "dict_pref", {"int_key": 10}) self._set_url("dict_pref") response = self.send_get(client) - assert u"{'int_key': 10}" == response.data + assert "{'int_key': 10}" == response.data def test_create_preference(self): """ @@ -470,8 +465,8 @@ class TestPreferencesDetailAPI(UserAPITestCase): self.client.login(username=self.user.username, password=TEST_PASSWORD) response = self.send_put(self.client, preference_value, expected_status=400) assert response.data ==\ - {'developer_message': u"Preference 'new_key' cannot be set to an empty value.", - 'user_message': u"Preference 'new_key' cannot be set to an empty value."} + {'developer_message': "Preference 'new_key' cannot be set to an empty value.", + 'user_message': "Preference 'new_key' cannot be set to an empty value."} self.send_get(self.client, expected_status=404) def test_create_preference_too_long_key(self): @@ -504,9 +499,9 @@ class TestPreferencesDetailAPI(UserAPITestCase): self.send_put(client, new_value, expected_status=403) @ddt.data( - (u"new value",), + ("new value",), (10,), - ({u"int_key": 10},) + ({"int_key": 10},) ) @ddt.unpack def test_update_preference(self, preference_value): @@ -516,7 +511,7 @@ class TestPreferencesDetailAPI(UserAPITestCase): self.client.login(username=self.user.username, password=TEST_PASSWORD) self.send_put(self.client, preference_value) response = self.send_get(self.client) - assert six.text_type(preference_value) == response.data + assert str(preference_value) == response.data @ddt.data( ("different_client", "different_user"), @@ -543,8 +538,8 @@ class TestPreferencesDetailAPI(UserAPITestCase): """ self.client.login(username=self.user.username, password=TEST_PASSWORD) response = self.send_put(self.client, preference_value, expected_status=400) - assert response.data == {'developer_message': u"Preference 'test_key' cannot be set to an empty value.", - 'user_message': u"Preference 'test_key' cannot be set to an empty value."} + assert response.data == {'developer_message': "Preference 'test_key' cannot be set to an empty value.", + 'user_message': "Preference 'test_key' cannot be set to an empty value."} response = self.send_get(self.client) assert self.test_pref_value == response.data diff --git a/openedx/core/djangoapps/user_api/verification_api/tests/test_views.py b/openedx/core/djangoapps/user_api/verification_api/tests/test_views.py index c06f96f2f7..aa211622cf 100644 --- a/openedx/core/djangoapps/user_api/verification_api/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/verification_api/tests/test_views.py @@ -132,7 +132,7 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase): 'status': self.photo_verification.status, 'expiration_datetime': '{}Z'.format(kwargs.get('expected_expires').isoformat()), 'message': '', - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': self.photo_verification.receipt_id, }] @@ -155,25 +155,25 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase): { 'type': 'Software Secure', 'status': self.photo_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': self.photo_verification.error_msg, - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': self.photo_verification.receipt_id }, { 'type': 'SSO', 'status': self.sso_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': '', - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': None, }, { 'type': 'Manual', 'status': self.manual_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': self.manual_verification.reason, - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': None, }, ] @@ -195,25 +195,25 @@ class VerificationsDetailsViewTests(VerificationStatusViewTestsMixin, TestCase): { 'type': 'Software Secure', 'status': self.photo_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': self.photo_verification.error_msg, - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': self.photo_verification.receipt_id, }, { 'type': 'Software Secure', 'status': second_ss_photo_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': second_ss_photo_verification.error_msg, - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': second_ss_photo_verification.receipt_id, }, { 'type': 'SSO', 'status': self.sso_verification.status, - 'expiration_datetime': '{}Z'.format(expected_expires.isoformat()), + 'expiration_datetime': f'{expected_expires.isoformat()}Z', 'message': '', - 'updated_at': '{}Z'.format(self.CREATED_AT.isoformat()), + 'updated_at': f'{self.CREATED_AT.isoformat()}Z', 'receipt_id': None, }, ]