diff --git a/openedx/core/djangoapps/xblock/api.py b/openedx/core/djangoapps/xblock/api.py index 94677fbcaf..9fde5ea29f 100644 --- a/openedx/core/djangoapps/xblock/api.py +++ b/openedx/core/djangoapps/xblock/api.py @@ -16,7 +16,6 @@ from django.utils.translation import ugettext as _ from opaque_keys.edx.keys import UsageKeyV2 from opaque_keys.edx.locator import BundleDefinitionLocator from rest_framework.exceptions import NotFound -import six from xblock.core import XBlock from xblock.exceptions import NoSuchViewError @@ -48,7 +47,7 @@ def get_runtime_system(): # and XBlock field data in general [does not distinguish between setting initial values during parsing and changing # values at runtime due to user interaction], and how it interacts with BlockstoreFieldData. Keeping the caches # local to each thread completely avoids this problem.) - cache_name = '_system_{}'.format(threading.get_ident()) + cache_name = f'_system_{threading.get_ident()}' if not hasattr(get_runtime_system, cache_name): params = dict( handler_url=get_handler_url, @@ -77,7 +76,7 @@ def load_block(usage_key, user): if user is not None and not context_impl.can_view_block(user, usage_key): # We do not know if the block was not found or if the user doesn't have # permission, but we want to return the same result in either case: - raise NotFound("XBlock {} does not exist, or you don't have permission to view it.".format(usage_key)) + raise NotFound(f"XBlock {usage_key} does not exist, or you don't have permission to view it.") # TODO: load field overrides from the context # e.g. a course might specify that all 'problem' XBlocks have 'max_attempts' @@ -106,7 +105,7 @@ def get_block_metadata(block, includes=()): children in other bundles. """ data = { - "block_id": six.text_type(block.scope_ids.usage_id), + "block_id": str(block.scope_ids.usage_id), "block_type": block.scope_ids.block_type, "display_name": get_block_display_name(block), } @@ -174,7 +173,7 @@ def get_block_display_name(block_or_key): def_key = resolve_definition(block_or_key) use_draft = get_xblock_app_config().get_learning_context_params().get('use_draft') cache = BundleCache(def_key.bundle_uuid, draft_name=use_draft) - cache_key = ('block_display_name', six.text_type(def_key)) + cache_key = ('block_display_name', str(def_key)) display_name = cache.get(cache_key) if display_name is None: # Instead of loading the block, just load its XML and parse it @@ -236,7 +235,7 @@ def get_handler_url(usage_key, handler_name, user): This view does not check/care if the XBlock actually exists. """ - usage_key_str = six.text_type(usage_key) + usage_key_str = str(usage_key) site_root_url = get_xblock_app_config().get_site_root_url() if not user: # lint-amnesty, pylint: disable=no-else-raise raise TypeError("Cannot get handler URLs without specifying a specific user ID.") diff --git a/openedx/core/djangoapps/xblock/learning_context/learning_context.py b/openedx/core/djangoapps/xblock/learning_context/learning_context.py index 16ac21e1b0..ed4f8d12a3 100644 --- a/openedx/core/djangoapps/xblock/learning_context/learning_context.py +++ b/openedx/core/djangoapps/xblock/learning_context/learning_context.py @@ -4,7 +4,7 @@ of content where learning happens. """ -class LearningContext(object): +class LearningContext: """ Abstract base class for learning context implementations. diff --git a/openedx/core/djangoapps/xblock/learning_context/manager.py b/openedx/core/djangoapps/xblock/learning_context/manager.py index 44f2f68a32..b7ed1c3c34 100644 --- a/openedx/core/djangoapps/xblock/learning_context/manager.py +++ b/openedx/core/djangoapps/xblock/learning_context/manager.py @@ -39,9 +39,9 @@ def get_learning_context_impl(key): context_type = key.context_key.CANONICAL_NAMESPACE elif isinstance(key, OpaqueKey): # Maybe this is an older modulestore key etc. - raise TypeError("Opaque key {} does not have a learning context.".format(key)) + raise TypeError(f"Opaque key {key} does not have a learning context.") else: - raise TypeError("key '{}' is not an opaque key. You probably forgot [KeyType].from_string(...)".format(key)) + raise TypeError(f"key '{key}' is not an opaque key. You probably forgot [KeyType].from_string(...)") try: return _learning_context_cache[context_type] diff --git a/openedx/core/djangoapps/xblock/runtime/blockstore_field_data.py b/openedx/core/djangoapps/xblock/runtime/blockstore_field_data.py index b4403473d3..ac17a9550b 100644 --- a/openedx/core/djangoapps/xblock/runtime/blockstore_field_data.py +++ b/openedx/core/djangoapps/xblock/runtime/blockstore_field_data.py @@ -26,7 +26,7 @@ CHILDREN_INCLUDES = Sentinel('CHILDREN_INCLUDES') # Key for a pseudo-field that MAX_DEFINITIONS_LOADED = 100 # How many of the most recently used XBlocks' field data to keep in memory at max. -class BlockInstanceUniqueKey(object): +class BlockInstanceUniqueKey: """ An empty object used as a unique key for each XBlock instance, see get_weak_key_for_block() and BlockstoreFieldData._get_active_block(). Every @@ -70,7 +70,7 @@ def get_olx_hash_for_definition_key(def_key): for entry in files_list: if entry.path == def_key.olx_path: return entry.hash_digest - raise NoSuchDefinition("Could not load OLX file for key {}".format(def_key)) + raise NoSuchDefinition(f"Could not load OLX file for key {def_key}") class BlockstoreFieldData(FieldData): @@ -103,7 +103,7 @@ class BlockstoreFieldData(FieldData): # (see _get_active_block()) and the value is an ActiveBlock object # (which holds olx_hash and changed_fields) self.active_blocks = WeakKeyDictionary() - super(BlockstoreFieldData, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() def _getfield(self, block, name): """ @@ -134,7 +134,7 @@ class BlockstoreFieldData(FieldData): raise InvalidScopeError("BlockstoreFieldData only supports UserScope.NONE fields") if field.scope.block not in (BlockScope.DEFINITION, BlockScope.USAGE): raise InvalidScopeError( - "BlockstoreFieldData does not support BlockScope.{} fields".format(field.scope.block) + f"BlockstoreFieldData does not support BlockScope.{field.scope.block} fields" ) # There is also BlockScope.TYPE but we don't need to support that; # it's mostly relevant as Scope.preferences(UserScope.ONE, BlockScope.TYPE) @@ -250,7 +250,7 @@ class BlockstoreFieldData(FieldData): as long as they're not in use. """ olx_hashes = set(self.loaded_definitions.keys()) - olx_hashes_needed = set(entry.olx_hash for entry in self.active_blocks.values()) + olx_hashes_needed = {entry.olx_hash for entry in self.active_blocks.values()} olx_hashes_safe_to_delete = olx_hashes - olx_hashes_needed @@ -278,7 +278,7 @@ class BlockstoreChildrenData(FieldData): """ # The data store that holds Scope.usage and Scope.definition data: self.authored_data_store = blockstore_field_data - super(BlockstoreChildrenData, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() def _check_field(self, block, name): # pylint: disable=unused-argument """ diff --git a/openedx/core/djangoapps/xblock/runtime/blockstore_runtime.py b/openedx/core/djangoapps/xblock/runtime/blockstore_runtime.py index e8fbf7a4a8..dcb59b1e6d 100644 --- a/openedx/core/djangoapps/xblock/runtime/blockstore_runtime.py +++ b/openedx/core/djangoapps/xblock/runtime/blockstore_runtime.py @@ -41,7 +41,7 @@ class BlockstoreXBlockRuntime(XBlockRuntime): """ def_id = self.id_reader.get_definition_id(usage_id) if def_id is None: - raise ValueError("Definition not found for usage {}".format(usage_id)) + raise ValueError(f"Definition not found for usage {usage_id}") if not isinstance(def_id, BundleDefinitionLocator): raise TypeError("This runtime can only load blocks stored in Blockstore bundles.") try: diff --git a/openedx/core/djangoapps/xblock/runtime/ephemeral_field_data.py b/openedx/core/djangoapps/xblock/runtime/ephemeral_field_data.py index bda64dde0e..b0f0ec8430 100644 --- a/openedx/core/djangoapps/xblock/runtime/ephemeral_field_data.py +++ b/openedx/core/djangoapps/xblock/runtime/ephemeral_field_data.py @@ -25,7 +25,7 @@ from xblock.runtime import KeyValueStore FIELD_DATA_TIMEOUT = None # keep in cache indefinitely, until cache needs pruning -class NotFound(object): +class NotFound: """ This class is a unique value that can be stored in a cache to indicate "not found" """ @@ -40,7 +40,7 @@ class EphemeralKeyValueStore(KeyValueStore): """ Expand the given XBlock key tuple to a format we can use as a key. """ - return u"ephemeral-xblock:{}".format(repr(tuple(key))) + return "ephemeral-xblock:{}".format(repr(tuple(key))) @property def _cache(self): diff --git a/openedx/core/djangoapps/xblock/runtime/mixin.py b/openedx/core/djangoapps/xblock/runtime/mixin.py index 968e96fefe..57eeb1d0df 100644 --- a/openedx/core/djangoapps/xblock/runtime/mixin.py +++ b/openedx/core/djangoapps/xblock/runtime/mixin.py @@ -37,11 +37,11 @@ class LmsBlockMixin(XBlockMixin): """ completion_service = self.runtime.service(self, 'completion') if completion_service is None: # lint-amnesty, pylint: disable=no-else-raise - raise JsonHandlerError(500, u"No completion service found") + raise JsonHandlerError(500, "No completion service found") elif not completion_service.completion_tracking_enabled(): - raise JsonHandlerError(404, u"Completion tracking is not enabled and API calls are unexpected") + raise JsonHandlerError(404, "Completion tracking is not enabled and API calls are unexpected") if not completion_service.can_mark_block_complete_on_view(self): - raise JsonHandlerError(400, u"Block not configured for completion on view.") + raise JsonHandlerError(400, "Block not configured for completion on view.") self.runtime.publish(self, "completion", data) return {'result': 'ok'} diff --git a/openedx/core/djangoapps/xblock/runtime/olx_parsing.py b/openedx/core/djangoapps/xblock/runtime/olx_parsing.py index 3abbed96b3..4bfb648d89 100644 --- a/openedx/core/djangoapps/xblock/runtime/olx_parsing.py +++ b/openedx/core/djangoapps/xblock/runtime/olx_parsing.py @@ -29,7 +29,7 @@ def parse_xblock_include(include_node): # Where "source" and "usage" are optional. if include_node.tag != 'xblock-include': # xss-lint: disable=python-wrap-html - raise BundleFormatException("Expected an XML node, but got <{}>".format(include_node.tag)) + raise BundleFormatException(f"Expected an XML node, but got <{include_node.tag}>") try: definition_path = include_node.attrib['definition'] except KeyError: @@ -41,7 +41,7 @@ def parse_xblock_include(include_node): try: block_type, definition_id = definition_path.split("/") except ValueError: - raise BundleFormatException("Invalid definition attribute: {}".format(definition_path)) # lint-amnesty, pylint: disable=raise-missing-from + raise BundleFormatException(f"Invalid definition attribute: {definition_path}") # lint-amnesty, pylint: disable=raise-missing-from return XBlockInclude(link_id=link_id, block_type=block_type, definition_id=definition_id, usage_hint=usage_hint) @@ -69,18 +69,18 @@ def definition_for_include(parsed_include, parent_definition_key): try: link = links[parsed_include.link_id] except KeyError: - raise BundleFormatException("Link not found: {}".format(parsed_include.link_id)) # lint-amnesty, pylint: disable=raise-missing-from + raise BundleFormatException(f"Link not found: {parsed_include.link_id}") # lint-amnesty, pylint: disable=raise-missing-from return BundleDefinitionLocator( bundle_uuid=link.bundle_uuid, block_type=parsed_include.block_type, - olx_path="{}/{}/definition.xml".format(parsed_include.block_type, parsed_include.definition_id), + olx_path=f"{parsed_include.block_type}/{parsed_include.definition_id}/definition.xml", bundle_version=link.version, ) else: return BundleDefinitionLocator( bundle_uuid=parent_definition_key.bundle_uuid, block_type=parsed_include.block_type, - olx_path="{}/{}/definition.xml".format(parsed_include.block_type, parsed_include.definition_id), + olx_path=f"{parsed_include.block_type}/{parsed_include.definition_id}/definition.xml", bundle_version=parent_definition_key.bundle_version, draft_name=parent_definition_key.draft_name, ) diff --git a/openedx/core/djangoapps/xblock/runtime/runtime.py b/openedx/core/djangoapps/xblock/runtime/runtime.py index 0a38d6db90..5f003e7479 100644 --- a/openedx/core/djangoapps/xblock/runtime/runtime.py +++ b/openedx/core/djangoapps/xblock/runtime/runtime.py @@ -3,6 +3,7 @@ Common base classes for all new XBlock runtimes. """ import logging +from urllib.parse import urljoin # pylint: disable=import-error import crum from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH @@ -12,7 +13,6 @@ from django.contrib.auth import get_user_model from django.core.exceptions import PermissionDenied from django.utils.lru_cache import lru_cache from eventtracking import tracker -from six.moves.urllib.parse import urljoin # pylint: disable=import-error from web_fragments.fragment import Fragment from xblock.exceptions import NoSuchServiceError from xblock.field_data import SplitFieldData @@ -72,7 +72,7 @@ class XBlockRuntime(RuntimeShim, Runtime): suppports_state_for_anonymous_users = True def __init__(self, system, user): - super(XBlockRuntime, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments + super().__init__( id_reader=system.id_reader, mixins=( LmsBlockMixin, # Adds Non-deprecated LMS/Studio functionality @@ -212,7 +212,7 @@ class XBlockRuntime(RuntimeShim, Runtime): # be removed from here and from XBlock.runtime declaration = block.service_declaration(service_name) if declaration is None: - raise NoSuchServiceError("Service {!r} was not requested.".format(service_name)) + raise NoSuchServiceError(f"Service {service_name!r} was not requested.") # Most common service is field-data so check that first: if service_name == "field-data": if block.scope_ids not in self.block_field_datas: @@ -233,7 +233,7 @@ class XBlockRuntime(RuntimeShim, Runtime): # Otherwise, fall back to the base implementation which loads services # defined in the constructor: if service is None: - service = super(XBlockRuntime, self).service(block, service_name) # lint-amnesty, pylint: disable=super-with-arguments + service = super().service(block, service_name) return service def _init_field_data_for_block(self, block): @@ -290,7 +290,7 @@ class XBlockRuntime(RuntimeShim, Runtime): # which create relative URLs (/static/studio/bundles/webpack-foo.js). # We want all resource URLs to be absolute, such as is done when # local_resource_url() is used. - fragment = super(XBlockRuntime, self).render(block, view_name, context) # lint-amnesty, pylint: disable=super-with-arguments + fragment = super().render(block, view_name, context) needs_fix = False for resource in fragment.resources: if resource.kind == 'url' and resource.data.startswith('/'): @@ -360,7 +360,7 @@ class XBlockRuntime(RuntimeShim, Runtime): return None -class XBlockRuntimeSystem(object): +class XBlockRuntimeSystem: """ This class is essentially a factory for XBlockRuntimes. This is a long-lived object which provides the behavior specific to the application diff --git a/openedx/core/djangoapps/xblock/runtime/shims.py b/openedx/core/djangoapps/xblock/runtime/shims.py index 01af86f3cf..e6b1d91da2 100644 --- a/openedx/core/djangoapps/xblock/runtime/shims.py +++ b/openedx/core/djangoapps/xblock/runtime/shims.py @@ -17,14 +17,14 @@ from common.djangoapps.edxmako.shortcuts import render_to_string from common.djangoapps.student.models import anonymous_id_for_user -class RuntimeShim(object): +class RuntimeShim: """ All the old/deprecated APIs that our XBlock runtime(s) need to support are captured in this mixin. """ def __init__(self, *args, **kwargs): - super(RuntimeShim, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self._active_block = None def render(self, block, view_name, context=None): @@ -40,7 +40,7 @@ class RuntimeShim(object): old_active_block = self._active_block self._active_block = block try: - return super(RuntimeShim, self).render(block, view_name, context) # lint-amnesty, pylint: disable=super-with-arguments + return super().render(block, view_name, context) finally: # Reset the active view to what it was before entering this method self._active_block = old_active_block @@ -53,7 +53,7 @@ class RuntimeShim(object): old_active_block = self._active_block self._active_block = block try: - return super(RuntimeShim, self).handle(block, handler_name, request, suffix) # lint-amnesty, pylint: disable=super-with-arguments + return super().handle(block, handler_name, request, suffix) finally: # Reset the active view to what it was before entering this method self._active_block = old_active_block @@ -358,7 +358,7 @@ class RuntimeShim(object): Get the list of CSS classes that the wrapping
should have for the specified xblock or aside's view. """ - css_classes = super(RuntimeShim, self)._css_classes_for(block, view) # lint-amnesty, pylint: disable=super-with-arguments + css_classes = super()._css_classes_for(block, view) # Many CSS styles for former XModules use # .xmodule_display.xmodule_VideoBlock # as their selector, so add those classes: @@ -366,11 +366,11 @@ class RuntimeShim(object): css_classes.append('xmodule_display') elif view == 'studio_view': css_classes.append('xmodule_edit') - css_classes.append('xmodule_{}'.format(block.unmixed_class.__name__)) + css_classes.append(f'xmodule_{block.unmixed_class.__name__}') return css_classes -class XBlockShim(object): +class XBlockShim: """ Mixin added to XBlock classes in this runtime, to support older/XModule APIs diff --git a/openedx/core/djangoapps/xblock/utils.py b/openedx/core/djangoapps/xblock/utils.py index 81794a0640..901598e010 100644 --- a/openedx/core/djangoapps/xblock/utils.py +++ b/openedx/core/djangoapps/xblock/utils.py @@ -10,7 +10,6 @@ from uuid import uuid4 import crum from django.conf import settings -from six import text_type # lint-amnesty, pylint: disable=unused-import def get_secure_token_for_xblock_handler(user_id, block_key_str, time_idx=0): diff --git a/openedx/core/djangoapps/xmodule_django/models.py b/openedx/core/djangoapps/xmodule_django/models.py index e15cf3ee17..071774e170 100644 --- a/openedx/core/djangoapps/xmodule_django/models.py +++ b/openedx/core/djangoapps/xmodule_django/models.py @@ -40,11 +40,11 @@ class NoneToEmptyQuerySet(models.query.QuerySet): direct = not field_object.auto_created or field_object.concrete if direct and hasattr(field_object, 'Empty'): for suffix in ('', '_exact'): - key = '{}{}'.format(field_object.name, suffix) + key = f'{field_object.name}{suffix}' if key in kwargs and kwargs[key] is None: kwargs[key] = field_object.Empty - return super(NoneToEmptyQuerySet, self)._filter_or_exclude(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super()._filter_or_exclude(*args, **kwargs) class OpaqueKeyField(opaque_keys.edx.django.models.OpaqueKeyField): @@ -61,7 +61,7 @@ class OpaqueKeyField(opaque_keys.edx.django.models.OpaqueKeyField): def __init__(self, *args, **kwargs): warnings.warn("openedx.core.djangoapps.xmodule_django.models.OpaqueKeyField is deprecated. " "Please use opaque_keys.edx.django.models.OpaqueKeyField instead.", stacklevel=2) - super(OpaqueKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) class CourseKeyField(opaque_keys.edx.django.models.CourseKeyField): @@ -71,7 +71,7 @@ class CourseKeyField(opaque_keys.edx.django.models.CourseKeyField): def __init__(self, *args, **kwargs): warnings.warn("openedx.core.djangoapps.xmodule_django.models.LocationKeyField is deprecated. " "Please use opaque_keys.edx.django.models.UsageKeyField instead.", stacklevel=2) - super(CourseKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) class UsageKeyField(opaque_keys.edx.django.models.UsageKeyField): @@ -81,7 +81,7 @@ class UsageKeyField(opaque_keys.edx.django.models.UsageKeyField): def __init__(self, *args, **kwargs): warnings.warn("openedx.core.djangoapps.xmodule_django.models.UsageKeyField is deprecated. " "Please use opaque_keys.edx.django.models.UsageKeyField instead.", stacklevel=2) - super(UsageKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) class UsageKeyWithRunField(opaque_keys.edx.django.models.UsageKeyField): @@ -90,7 +90,7 @@ class UsageKeyWithRunField(opaque_keys.edx.django.models.UsageKeyField): missing `run` values, for old Mongo courses. """ def to_python(self, value): - value = super(UsageKeyWithRunField, self).to_python(value) # lint-amnesty, pylint: disable=super-with-arguments + value = super().to_python(value) if value is not None and value.run is None: value = value.replace(course_key=modulestore().fill_in_run(value.course_key)) return value @@ -103,4 +103,4 @@ class BlockTypeKeyField(opaque_keys.edx.django.models.BlockTypeKeyField): def __init__(self, *args, **kwargs): warnings.warn("openedx.core.djangoapps.xmodule_django.models.BlockTypeKeyField is deprecated. " "Please use opaque_keys.edx.django.models.BlockTypeKeyField instead.", stacklevel=2) - super(BlockTypeKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) diff --git a/openedx/core/djangoapps/zendesk_proxy/apps.py b/openedx/core/djangoapps/zendesk_proxy/apps.py index 411eb065f1..e2d6f8a57a 100644 --- a/openedx/core/djangoapps/zendesk_proxy/apps.py +++ b/openedx/core/djangoapps/zendesk_proxy/apps.py @@ -19,24 +19,24 @@ class ZendeskProxyConfig(AppConfig): plugin_app = { PluginURLs.CONFIG: { ProjectType.CMS: { - PluginURLs.NAMESPACE: u'', + PluginURLs.NAMESPACE: '', PluginURLs.REGEX: r'^zendesk_proxy/', - PluginURLs.RELATIVE_PATH: u'urls', + PluginURLs.RELATIVE_PATH: 'urls', }, ProjectType.LMS: { - PluginURLs.NAMESPACE: u'', + PluginURLs.NAMESPACE: '', PluginURLs.REGEX: r'^zendesk_proxy/', - PluginURLs.RELATIVE_PATH: u'urls', + PluginURLs.RELATIVE_PATH: 'urls', } }, PluginSettings.CONFIG: { ProjectType.CMS: { - SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'}, - SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: u'settings.production'}, + SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'}, + SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'}, }, ProjectType.LMS: { - SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'}, - SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: u'settings.production'}, + SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'}, + SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'}, } } } diff --git a/openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py b/openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py index d0955ab432..9888487a98 100644 --- a/openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py +++ b/openedx/core/djangoapps/zendesk_proxy/tests/test_utils.py @@ -6,10 +6,10 @@ Tests of Zendesk interaction utility functions import json from collections import OrderedDict +from unittest.mock import MagicMock, patch from django.test.utils import override_settings import ddt -from mock import MagicMock, patch from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket from openedx.core.lib.api.test_utils import ApiTestCase @@ -28,7 +28,7 @@ class TestUtils(ApiTestCase): # lint-amnesty, pylint: disable=missing-class-doc 'subject': 'Python Unit Test Help Request', 'body': "Help! I'm trapped in a unit test factory and I can't get out!", } - return super(TestUtils, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + return super().setUp() @override_settings( ZENDESK_URL=None, diff --git a/openedx/core/djangoapps/zendesk_proxy/tests/test_v0_views.py b/openedx/core/djangoapps/zendesk_proxy/tests/test_v0_views.py index 9a33864b64..bf3a7cc24e 100644 --- a/openedx/core/djangoapps/zendesk_proxy/tests/test_v0_views.py +++ b/openedx/core/djangoapps/zendesk_proxy/tests/test_v0_views.py @@ -4,12 +4,10 @@ from copy import deepcopy import json +from unittest.mock import MagicMock, patch import ddt from django.urls import reverse from django.test.utils import override_settings -from mock import MagicMock, patch -import six -from six.moves import range from openedx.core.djangoapps.zendesk_proxy.v0.views import ZENDESK_REQUESTS_PER_HOUR from openedx.core.lib.api.test_utils import ApiTestCase @@ -34,7 +32,7 @@ class ZendeskProxyTestCase(ApiTestCase): 'message': "Help! I'm trapped in a unit test factory and I can't get out!", } } - return super(ZendeskProxyTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + return super().setUp() def test_post(self): with patch('requests.post', return_value=MagicMock(status_code=201)) as mock_post: @@ -47,7 +45,7 @@ class ZendeskProxyTestCase(ApiTestCase): self.assertHttpCreated(response) (mock_args, mock_kwargs) = mock_post.call_args assert mock_args == ('https://www.superrealurlsthataredefinitelynotfake.com/api/v2/tickets.json',) - six.assertCountEqual(self, mock_kwargs.keys(), ['headers', 'data']) + self.assertCountEqual(mock_kwargs.keys(), ['headers', 'data']) assert mock_kwargs['headers'] == { 'content-type': 'application/json', 'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890' } diff --git a/openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py b/openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py index b3e6d9dc39..10ce4e2afc 100644 --- a/openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py +++ b/openedx/core/djangoapps/zendesk_proxy/tests/test_v1_views.py @@ -3,13 +3,11 @@ from copy import deepcopy import json +from unittest.mock import MagicMock, patch import ddt from django.urls import reverse from django.test.utils import override_settings -from mock import MagicMock, patch -import six -from six.moves import range from common.djangoapps.student.tests.factories import UserFactory from openedx.core.djangoapps.zendesk_proxy.v1.views import ZendeskProxyThrottle @@ -45,7 +43,7 @@ class ZendeskProxyTestCase(ApiTestCase): } ], } - return super(ZendeskProxyTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + return super().setUp() @ddt.data( True, False @@ -67,7 +65,7 @@ class ZendeskProxyTestCase(ApiTestCase): self.assertHttpCreated(response) (mock_args, mock_kwargs) = mock_post.call_args assert mock_args == ('https://www.superrealurlsthataredefinitelynotfake.com/api/v2/tickets.json',) - six.assertCountEqual(self, mock_kwargs.keys(), ['headers', 'data']) + self.assertCountEqual(mock_kwargs.keys(), ['headers', 'data']) assert mock_kwargs['headers'] == { 'content-type': 'application/json', 'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890' } diff --git a/openedx/core/djangoapps/zendesk_proxy/utils.py b/openedx/core/djangoapps/zendesk_proxy/utils.py index d6d95f2b7b..9401949dc9 100644 --- a/openedx/core/djangoapps/zendesk_proxy/utils.py +++ b/openedx/core/djangoapps/zendesk_proxy/utils.py @@ -5,24 +5,24 @@ Utility functions for zendesk interaction. import json import logging +from urllib.parse import urljoin # pylint: disable=import-error import requests from django.conf import settings from rest_framework import status -from six.moves.urllib.parse import urljoin # pylint: disable=import-error log = logging.getLogger(__name__) def _std_error_message(details, payload): """Internal helper to standardize error message. This allows for simpler splunk alerts.""" - return u'zendesk_proxy action required\n{}\nNo ticket created for payload {}'.format(details, payload) + return f'zendesk_proxy action required\n{details}\nNo ticket created for payload {payload}' def _get_request_headers(): return { 'content-type': 'application/json', - 'Authorization': u"Bearer {}".format(settings.ZENDESK_OAUTH_ACCESS_TOKEN), + 'Authorization': f"Bearer {settings.ZENDESK_OAUTH_ACCESS_TOKEN}", } @@ -72,7 +72,7 @@ def create_zendesk_ticket( group_id = settings.ZENDESK_GROUP_ID_MAPPING[group] data['ticket']['group_id'] = group_id else: - msg = u"Group ID not found for group {}. Please update ZENDESK_GROUP_ID_MAPPING".format(group) + msg = f"Group ID not found for group {group}. Please update ZENDESK_GROUP_ID_MAPPING" log.error(_std_error_message(msg, payload)) return status.HTTP_400_BAD_REQUEST @@ -84,11 +84,11 @@ def create_zendesk_ticket( # Check for HTTP codes other than 201 (Created) if response.status_code == status.HTTP_201_CREATED: - log.debug(u'Successfully created ticket for {}'.format(requester_email)) + log.debug(f'Successfully created ticket for {requester_email}') else: log.error( _std_error_message( - u'Unexpected response: {} - {}'.format(response.status_code, response.content), + f'Unexpected response: {response.status_code} - {response.content}', payload ) ) @@ -98,8 +98,8 @@ def create_zendesk_ticket( except (ValueError, KeyError): log.error( _std_error_message( - u"Got an unexpected response from zendesk api. Can't" - u" get the ticket number to add extra info. {}".format(additional_info), + "Got an unexpected response from zendesk api. Can't" + " get the ticket number to add extra info. {}".format(additional_info), response.content ) ) @@ -118,8 +118,8 @@ def post_additional_info_as_comment(ticket_id, additional_info): to management and not students. """ additional_info_string = ( - u"Additional information:\n\n" + - u"\n".join(u"%s: %s" % (key, value) for (key, value) in additional_info.items() if value is not None) + "Additional information:\n\n" + + "\n".join(f"{key}: {value}" for (key, value) in additional_info.items() if value is not None) ) data = { @@ -131,16 +131,16 @@ def post_additional_info_as_comment(ticket_id, additional_info): } } - url = urljoin(settings.ZENDESK_URL, 'api/v2/tickets/{}.json'.format(ticket_id)) + url = urljoin(settings.ZENDESK_URL, f'api/v2/tickets/{ticket_id}.json') try: response = requests.put(url, data=json.dumps(data), headers=_get_request_headers()) if response.status_code == 200: - log.debug(u'Successfully created comment for ticket {}'.format(ticket_id)) + log.debug(f'Successfully created comment for ticket {ticket_id}') else: log.error( _std_error_message( - u'Unexpected response: {} - {}'.format(response.status_code, response.content), + f'Unexpected response: {response.status_code} - {response.content}', data ) ) diff --git a/openedx/core/djangoapps/zendesk_proxy/v0/views.py b/openedx/core/djangoapps/zendesk_proxy/v0/views.py index a7a72b16de..c52c0039f4 100644 --- a/openedx/core/djangoapps/zendesk_proxy/v0/views.py +++ b/openedx/core/djangoapps/zendesk_proxy/v0/views.py @@ -18,8 +18,8 @@ class ZendeskProxyThrottle(SimpleRateThrottle): Custom throttle rates for this particular endpoint's use case. """ def __init__(self): - self.rate = '{}/hour'.format(ZENDESK_REQUESTS_PER_HOUR) - super(ZendeskProxyThrottle, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + self.rate = f'{ZENDESK_REQUESTS_PER_HOUR}/hour' + super().__init__() def get_cache_key(self, request, view): """ diff --git a/openedx/core/djangoapps/zendesk_proxy/v1/views.py b/openedx/core/djangoapps/zendesk_proxy/v1/views.py index ec08ec465e..e99b1c6409 100644 --- a/openedx/core/djangoapps/zendesk_proxy/v1/views.py +++ b/openedx/core/djangoapps/zendesk_proxy/v1/views.py @@ -24,8 +24,8 @@ class ZendeskProxyThrottle(UserRateThrottle): """ def __init__(self): - self.rate = '{}/hour'.format(REQUESTS_PER_HOUR) - super(ZendeskProxyThrottle, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + self.rate = f'{REQUESTS_PER_HOUR}/hour' + super().__init__() class ZendeskPassthroughView(APIView):