diff --git a/cms/djangoapps/xblock_config/tests/test_models.py b/cms/djangoapps/xblock_config/tests/test_models.py index fbd379c584..3ce81a51f1 100644 --- a/cms/djangoapps/xblock_config/tests/test_models.py +++ b/cms/djangoapps/xblock_config/tests/test_models.py @@ -5,9 +5,9 @@ from contextlib import contextmanager import ddt from django.test import TestCase -from edx_django_utils.cache import RequestCache from opaque_keys.edx.locator import CourseLocator +from openedx.core.djangoapps.request_cache.middleware import RequestCache from xblock_config.models import CourseEditLTIFieldsEnabledFlag @@ -20,7 +20,7 @@ def lti_consumer_fields_editing_flag(course_id, enabled_for_course=False): course_id (CourseLocator): course locator to control this feature for. enabled_for_course (bool): whether feature is enabled for 'course_id' """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() CourseEditLTIFieldsEnabledFlag.objects.create(course_id=course_id, enabled=enabled_for_course) yield diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index 3361d23c65..7a03b4c196 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -13,12 +13,11 @@ from django.db.models import Q from django.dispatch import receiver from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ -from edx_django_utils.cache import RequestCache from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.django.models import CourseKeyField from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.request_cache.middleware import ns_request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, ns_request_cached Mode = namedtuple('Mode', [ @@ -728,7 +727,7 @@ class CourseMode(models.Model): @receiver(models.signals.post_delete, sender=CourseMode) def invalidate_course_mode_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of course modes. """ - RequestCache(namespace=CourseMode.CACHE_NAMESPACE).clear() + RequestCache.clear_request_cache(name=CourseMode.CACHE_NAMESPACE) def get_cosmetic_verified_display_price(course): diff --git a/common/djangoapps/edxmako/tests.py b/common/djangoapps/edxmako/tests.py index 4f96e680e2..23e3065705 100644 --- a/common/djangoapps/edxmako/tests.py +++ b/common/djangoapps/edxmako/tests.py @@ -7,12 +7,12 @@ from django.http import HttpResponse from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings -from edx_django_utils.cache import RequestCache from mock import Mock, patch from edxmako import LOOKUP, add_lookup from edxmako.request_context import get_template_request_context from edxmako.shortcuts import is_any_marketing_link_set, is_marketing_link_set, marketing_link, render_to_string +from openedx.core.djangoapps.request_cache.middleware import RequestCache from student.tests.factories import UserFactory from util.testing import UrlResetMixin @@ -89,7 +89,7 @@ class MakoRequestContextTest(TestCase): self.request.user = self.user self.response = Mock(spec=HttpResponse) - self.addCleanup(RequestCache.clear_all_namespaces) + self.addCleanup(RequestCache.clear_request_cache) def test_with_current_request(self): """ @@ -128,7 +128,7 @@ class MakoRequestContextTest(TestCase): self.assertIsNotNone(get_template_request_context()) mock_get_current_request.assert_not_called() - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() with patch('edxmako.request_context.get_current_request', return_value=None): # requestcontext should be None, because the cache isn't filled diff --git a/common/lib/xmodule/xmodule/modulestore/django.py b/common/lib/xmodule/xmodule/modulestore/django.py index 0013cac544..5fb1f9921f 100644 --- a/common/lib/xmodule/xmodule/modulestore/django.py +++ b/common/lib/xmodule/xmodule/modulestore/django.py @@ -23,8 +23,8 @@ from django.core.cache import caches, InvalidCacheBackendError import django.dispatch import django.utils from django.utils.translation import get_language, to_locale -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE +from openedx.core.djangoapps.request_cache.middleware import RequestCache from xmodule.contentstore.django import contentstore from xmodule.modulestore.draft_and_published import BranchSettingMixin from xmodule.modulestore.mixed import MixedModuleStore @@ -249,7 +249,7 @@ def create_modulestore_instance( if key in _options and isinstance(_options[key], basestring): _options[key] = load_function(_options[key]) - request_cache = DEFAULT_REQUEST_CACHE + request_cache = RequestCache.get_request_cache() try: metadata_inheritance_cache = caches['mongo_metadata_inheritance'] @@ -278,9 +278,14 @@ def create_modulestore_instance( if disabled_xblocks is None: return [] - if 'disabled_xblock_types' not in request_cache.data: - request_cache.data['disabled_xblock_types'] = [block.name for block in disabled_xblocks()] - return request_cache.data['disabled_xblock_types'] + if request_cache: + if 'disabled_xblock_types' not in request_cache.data: + request_cache.data['disabled_xblock_types'] = [block.name for block in disabled_xblocks()] + return request_cache.data['disabled_xblock_types'] + else: + disabled_xblock_types = [block.name for block in disabled_xblocks()] + + return disabled_xblock_types return class_( contentstore=content_store, diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index 3588600594..c76dfebfce 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -16,12 +16,12 @@ from django.conf import settings from django.core.cache import caches from django.test.client import RequestFactory from django.test.utils import override_settings -from edx_django_utils.cache import RequestCache from lms.djangoapps.ccx.tests.factories import CcxFactory from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.block_structure.api import get_course_in_cache from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from pytz import UTC +from openedx.core.djangoapps.request_cache.middleware import RequestCache from student.models import CourseEnrollment from student.tests.factories import UserFactory from xblock.core import XBlock @@ -176,7 +176,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT get_course_in_cache(course_key) # We clear the request cache to simulate a new request in the LMS. - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() # Reset the list of provider classes, so that our django settings changes # can actually take affect. diff --git a/lms/djangoapps/ccx/tests/test_overrides.py b/lms/djangoapps/ccx/tests/test_overrides.py index 1665fef9f0..536f26fc97 100644 --- a/lms/djangoapps/ccx/tests/test_overrides.py +++ b/lms/djangoapps/ccx/tests/test_overrides.py @@ -8,7 +8,6 @@ import mock import pytz from ccx_keys.locator import CCXLocator from django.test.utils import override_settings -from edx_django_utils.cache import RequestCache from courseware.courses import get_course_by_id from courseware.field_overrides import OverrideFieldData @@ -17,6 +16,7 @@ from lms.djangoapps.ccx.models import CustomCourseForEdX from lms.djangoapps.ccx.overrides import override_field_for_ccx from lms.djangoapps.ccx.tests.utils import flatten, iter_blocks from lms.djangoapps.courseware.tests.test_field_overrides import inject_field_overrides +from openedx.core.djangoapps.request_cache.middleware import RequestCache from student.tests.factories import AdminFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -74,7 +74,7 @@ class TestFieldOverrides(FieldOverrideTestMixin, SharedModuleStoreTestCase): get_ccx.return_value = ccx self.addCleanup(patch.stop) - self.addCleanup(RequestCache.clear_all_namespaces) + self.addCleanup(RequestCache.clear_request_cache) inject_field_overrides(iter_blocks(ccx.course), self.course, AdminFactory.create()) diff --git a/lms/djangoapps/ccx/tests/test_views.py b/lms/djangoapps/ccx/tests/test_views.py index ef2a7efe1d..1ab1b829a9 100644 --- a/lms/djangoapps/ccx/tests/test_views.py +++ b/lms/djangoapps/ccx/tests/test_views.py @@ -26,7 +26,6 @@ from courseware.testutils import FieldOverrideTestMixin from django_comment_client.utils import has_forum_access from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR from django_comment_common.utils import are_permissions_roles_seeded -from edx_django_utils.cache import RequestCache from edxmako.shortcuts import render_to_response from lms.djangoapps.ccx.models import CustomCourseForEdX from lms.djangoapps.ccx.overrides import get_override_for_ccx, override_field_for_ccx @@ -37,6 +36,7 @@ from lms.djangoapps.ccx.views import get_date from lms.djangoapps.grades.tasks import compute_all_grades_for_course from lms.djangoapps.instructor.access import allow_access, list_with_level from openedx.core.djangoapps.content.course_overviews.models import CourseOverview +from openedx.core.djangoapps.request_cache.middleware import RequestCache from openedx.core.lib.tests import attr from student.models import CourseEnrollment, CourseEnrollmentAllowed from student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole @@ -1051,7 +1051,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro CourseOverview.load_from_module_store(self.course.id) setup_students_and_grades(self) self.client.login(username=coach.username, password="test") - self.addCleanup(RequestCache.clear_all_namespaces) + self.addCleanup(RequestCache.clear_request_cache) from xmodule.modulestore.django import SignalHandler # using CCX object as sender here. @@ -1064,7 +1064,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro @patch('lms.djangoapps.instructor.views.gradebook_api.MAX_STUDENTS_PER_PAGE_GRADE_BOOK', 1) def test_gradebook(self): self.course.enable_ccx = True - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() url = reverse( 'ccx_gradebook', @@ -1081,7 +1081,7 @@ class TestCCXGrades(FieldOverrideTestMixin, SharedModuleStoreTestCase, LoginEnro def test_grades_csv(self): self.course.enable_ccx = True - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() url = reverse( 'ccx_grades_csv', diff --git a/lms/djangoapps/commerce/signals.py b/lms/djangoapps/commerce/signals.py index a6b53adb28..54e3134053 100644 --- a/lms/djangoapps/commerce/signals.py +++ b/lms/djangoapps/commerce/signals.py @@ -5,11 +5,11 @@ from __future__ import unicode_literals import logging -from crum import get_current_request from django.contrib.auth.models import AnonymousUser from django.dispatch import receiver from openedx.core.djangoapps.commerce.utils import is_commerce_service_configured +from openedx.core.djangoapps.request_cache.middleware import RequestCache from student.signals import REFUND_ORDER from .utils import refund_seat @@ -56,5 +56,5 @@ def get_request_user(): If the requester of an unenrollment is not the same person as the student being unenrolled, we authenticate to the commerce service as the requester. """ - request = get_current_request() + request = RequestCache.get_current_request() return getattr(request, 'user', None) diff --git a/lms/djangoapps/courseware/course_tools.py b/lms/djangoapps/courseware/course_tools.py index 8493cbd7d3..25747b374a 100644 --- a/lms/djangoapps/courseware/course_tools.py +++ b/lms/djangoapps/courseware/course_tools.py @@ -4,13 +4,13 @@ Platform plugins to support a verified upgrade tool. import datetime import pytz -from crum import get_current_request from django.utils.translation import ugettext as _ from course_modes.models import CourseMode from openedx.features.course_experience.course_tools import CourseTool from student.models import CourseEnrollment from courseware.date_summary import verified_upgrade_deadline_link +from openedx.core.djangoapps.request_cache import get_request class VerifiedUpgradeTool(CourseTool): @@ -69,5 +69,5 @@ class VerifiedUpgradeTool(CourseTool): """ Returns the URL for this tool for the specified course key. """ - request = get_current_request() + request = get_request() return verified_upgrade_deadline_link(request.user, course_id=course_key) diff --git a/lms/djangoapps/courseware/field_overrides.py b/lms/djangoapps/courseware/field_overrides.py index ae172064d6..dd57b5af6f 100644 --- a/lms/djangoapps/courseware/field_overrides.py +++ b/lms/djangoapps/courseware/field_overrides.py @@ -19,9 +19,9 @@ from abc import ABCMeta, abstractmethod from contextlib import contextmanager from django.conf import settings -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE from xblock.field_data import FieldData +from openedx.core.djangoapps.request_cache.middleware import RequestCache from xmodule.modulestore.inheritance import InheritanceMixin NOTSET = object() @@ -180,7 +180,7 @@ class OverrideFieldData(FieldData): Arguments: course: The course XBlock """ - request_cache = DEFAULT_REQUEST_CACHE + request_cache = RequestCache.get_request_cache() if course is None: cache_key = ENABLED_OVERRIDE_PROVIDERS_KEY.format(course_id='None') else: @@ -294,7 +294,7 @@ class OverrideModulestoreFieldData(OverrideFieldData): course_id = unicode(block.location.course_key) cache_key = ENABLED_MODULESTORE_OVERRIDE_PROVIDERS_KEY.format(course_id=course_id) - request_cache = DEFAULT_REQUEST_CACHE + request_cache = RequestCache.get_request_cache() enabled_providers = request_cache.data.get(cache_key) if enabled_providers is None: diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 577fdb4086..4cbd404ebe 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -2414,7 +2414,7 @@ class TestDisabledXBlockTypes(ModuleStoreTestCase): self._verify_descriptor('problem', course, 'CapaDescriptorWithMixins', item_usage_id) # Now simulate a new request cache. - self.store.request_cache.data.clear() + self.store.request_cache.data = {} self._verify_descriptor('problem', course, 'RawDescriptorWithMixins', item_usage_id) def _verify_descriptor(self, category, course, descriptor, item_id=None): diff --git a/lms/djangoapps/discussion/tests/test_views.py b/lms/djangoapps/discussion/tests/test_views.py index 6bc60fa237..dd29291dc3 100644 --- a/lms/djangoapps/discussion/tests/test_views.py +++ b/lms/djangoapps/discussion/tests/test_views.py @@ -8,7 +8,6 @@ from django.http import Http404 from django.test.client import Client, RequestFactory from django.test.utils import override_settings from django.utils import translation -from edx_django_utils.cache import RequestCache from mock import ANY, Mock, call, patch from six import text_type @@ -44,6 +43,7 @@ from lms.lib.comment_client.utils import CommentClientPaginatedResult from openedx.core.djangoapps.course_groups.models import CourseUserGroup from openedx.core.djangoapps.course_groups.tests.helpers import config_course_cohorts from openedx.core.djangoapps.course_groups.tests.test_views import CohortViewsTestCase +from openedx.core.djangoapps.request_cache.middleware import RequestCache from openedx.core.djangoapps.util.testing import ContentGroupTestCase from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseTestConsentRequired @@ -1968,7 +1968,7 @@ class CourseDiscussionsHandlerTestCase(DividedDiscussionsTestCase): expected_response = self.get_expected_response() self.assertEqual(response, expected_response) - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() now = datetime.now() # inline discussion ItemFactory.create( diff --git a/lms/djangoapps/django_comment_client/permissions.py b/lms/djangoapps/django_comment_client/permissions.py index 8dba693a2a..9527182696 100644 --- a/lms/djangoapps/django_comment_client/permissions.py +++ b/lms/djangoapps/django_comment_client/permissions.py @@ -5,19 +5,18 @@ Module for checking permissions with the comment_client backend import logging from types import NoneType -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE from opaque_keys.edx.keys import CourseKey from django_comment_common.models import CourseDiscussionSettings, all_permissions_for_user_in_course from django_comment_common.utils import get_course_discussion_settings from lms.djangoapps.teams.models import CourseTeam from lms.lib.comment_client import Thread -from openedx.core.djangoapps.request_cache.middleware import request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, request_cached def has_permission(user, permission, course_id=None): assert isinstance(course_id, (NoneType, CourseKey)) - request_cache_dict = DEFAULT_REQUEST_CACHE.data + request_cache_dict = RequestCache.get_request_cache().data cache_key = "django_comment_client.permissions.has_permission.all_permissions.{}.{}".format( user.id, course_id ) @@ -66,7 +65,7 @@ def _check_condition(user, condition, content): if not content: return False try: - request_cache_dict = DEFAULT_REQUEST_CACHE.data + request_cache_dict = RequestCache.get_request_cache().data if content["type"] == "thread": cache_key = "django_comment_client.permissions._check_condition.check_question_author.{}.{}".format( user.id, content['id'] @@ -99,7 +98,7 @@ def _check_condition(user, condition, content): return False try: commentable_id = content['commentable_id'] - request_cache_dict = DEFAULT_REQUEST_CACHE.data + request_cache_dict = RequestCache.get_request_cache().data cache_key = u"django_comment_client.check_team_member.{}.{}".format(user.id, commentable_id) if cache_key in request_cache_dict: return request_cache_dict[cache_key] diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py index d9d126aefc..0573427058 100644 --- a/lms/djangoapps/django_comment_client/tests/test_utils.py +++ b/lms/djangoapps/django_comment_client/tests/test_utils.py @@ -7,7 +7,6 @@ import mock from django.urls import reverse from django.test import RequestFactory, TestCase -from edx_django_utils.cache import RequestCache from mock import Mock, patch from pytz import UTC from six import text_type @@ -37,6 +36,7 @@ from lms.lib.comment_client.utils import CommentClientMaintenanceError, perform_ from openedx.core.djangoapps.course_groups import cohorts from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory, config_course_cohorts +from openedx.core.djangoapps.request_cache.middleware import RequestCache from openedx.core.djangoapps.util.testing import ContentGroupTestCase from openedx.core.lib.tests import attr from student.roles import CourseStaffRole @@ -217,7 +217,7 @@ class CoursewareContextTestCase(ModuleStoreTestCase): self.assertEqual(len(utils.get_accessible_discussion_xblocks(course, self.user)), 1) # The above call is request cached, so we need to clear it for this test. - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() # Add an orphan discussion xblock to that course orphan = course.id.make_usage_key('discussion', 'orphan_discussion') self.store.create_item(self.user.id, orphan.course_key, orphan.block_type, block_id=orphan.block_id) @@ -261,7 +261,7 @@ class CachedDiscussionIdMapTestCase(ModuleStoreTestCase): discussion_target='Beta Testing', visible_to_staff_only=True ) - RequestCache.clear_all_namespaces() # clear the cache before the last course publish + RequestCache.clear_request_cache() # clear the cache before the last course publish self.bad_discussion = ItemFactory.create( parent_location=self.course.location, category='discussion', @@ -1780,7 +1780,7 @@ class GroupModeratorPermissionsTestCase(ModuleStoreTestCase): 'can_vote': True, 'can_report': True }) - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() set_discussion_division_settings(self.course.id, division_scheme=CourseDiscussionSettings.ENROLLMENT_TRACK) content = {'user_id': self.verified_user.id, 'type': 'thread', 'username': self.verified_user.username} diff --git a/lms/djangoapps/gating/tests/test_integration.py b/lms/djangoapps/gating/tests/test_integration.py index 45dfa148ae..66da7e9d07 100644 --- a/lms/djangoapps/gating/tests/test_integration.py +++ b/lms/djangoapps/gating/tests/test_integration.py @@ -4,7 +4,6 @@ Integration tests for gated content. import ddt from crum import set_current_request from completion import waffle as completion_waffle -from edx_django_utils.cache import RequestCache from milestones import api as milestones_api from milestones.tests.utils import MilestonesTestCaseMixin @@ -13,6 +12,7 @@ from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.grades.tests.utils import answer_problem from openedx.core.djangolib.testing.utils import get_mock_request from openedx.core.lib.gating import api as gating_api +from openedx.core.djangoapps.request_cache.middleware import RequestCache from student.tests.factories import UserFactory from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase @@ -141,7 +141,7 @@ class TestGatedContent(MilestonesTestCaseMixin, SharedModuleStoreTestCase): Verifies access to gated content for the given user is as expected. """ # clear the request cache to flush any cached access results - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() # access to gating content (seq1) remains constant self.assertTrue(bool(has_access(user, 'load', self.seq1, self.course.id))) diff --git a/lms/djangoapps/grades/config/tests/utils.py b/lms/djangoapps/grades/config/tests/utils.py index b038eba233..89a910c31b 100644 --- a/lms/djangoapps/grades/config/tests/utils.py +++ b/lms/djangoapps/grades/config/tests/utils.py @@ -3,9 +3,9 @@ Provides helper functions for tests that want to configure flags related to persistent grading. """ from contextlib import contextmanager -from edx_django_utils.cache import RequestCache from lms.djangoapps.grades.config.models import CoursePersistentGradesFlag, PersistentGradesEnabledFlag +from openedx.core.djangoapps.request_cache.middleware import RequestCache @contextmanager @@ -20,7 +20,7 @@ def persistent_grades_feature_flags( as they need to set the global setting and the course-specific setting for a single course. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() PersistentGradesEnabledFlag.objects.create(enabled=global_flag, enabled_for_all_courses=enabled_for_all_courses) if course_id: CoursePersistentGradesFlag.objects.create(course_id=course_id, enabled=enabled_for_course) diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index 654d97dd28..403d761ba1 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -24,7 +24,6 @@ from courseware.tests.factories import InstructorFactory from django.conf import settings from django.urls import reverse from django.test.utils import override_settings -from edx_django_utils.cache import RequestCache from freezegun import freeze_time from instructor_analytics.basic import UNAVAILABLE, list_problem_responses from mock import MagicMock, Mock, patch, ANY @@ -86,6 +85,7 @@ from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVer from openedx.core.djangoapps.course_groups.models import CohortMembership, CourseUserGroupPartitionGroup from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory from openedx.core.djangoapps.credit.tests.factories import CreditCourseFactory +from openedx.core.djangoapps.request_cache.middleware import RequestCache from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme from openedx.core.djangoapps.util.testing import ContentGroupTestCase, TestConditionalContent from ..models import ReportStore @@ -411,7 +411,7 @@ class TestInstructorGradeReport(InstructorGradeReportTestCase): CourseEnrollment.enroll(user, course.id, mode='verified') SoftwareSecurePhotoVerificationFactory.create(user=user, status='approved') - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() expected_query_count = 43 with patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task'): diff --git a/lms/djangoapps/lms_xblock/runtime.py b/lms/djangoapps/lms_xblock/runtime.py index 67bc712cfb..ce3b8cf221 100644 --- a/lms/djangoapps/lms_xblock/runtime.py +++ b/lms/djangoapps/lms_xblock/runtime.py @@ -4,7 +4,6 @@ Module implementing `xblock.runtime.Runtime` functionality for the LMS from completion.services import CompletionService from django.conf import settings from django.urls import reverse -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE import xblock.reference.plugins from badges.service import BadgingService @@ -13,6 +12,7 @@ from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig from openedx.core.djangoapps.user_api.course_tag import api as user_course_tag_api from openedx.core.lib.url_utils import quote_slashes from openedx.core.lib.xblock_utils import xblock_local_resource_url +from openedx.core.djangoapps.request_cache.middleware import RequestCache from xmodule.library_tools import LibraryToolsService from xmodule.modulestore.django import ModuleI18nService, modulestore from xmodule.partitions.partitions_service import PartitionService @@ -133,7 +133,7 @@ class LmsModuleSystem(ModuleSystem): # pylint: disable=abstract-method ModuleSystem specialized to the LMS """ def __init__(self, **kwargs): - request_cache_dict = DEFAULT_REQUEST_CACHE.data + request_cache_dict = RequestCache.get_request_cache().data store = modulestore() services = kwargs.setdefault('services', {}) diff --git a/lms/envs/common.py b/lms/envs/common.py index a3fae92053..4e7afd9d30 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3259,6 +3259,8 @@ AUDIT_CERT_CUTOFF_DATE = None CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user' CREDENTIALS_GENERATION_ROUTING_KEY = HIGH_PRIORITY_QUEUE +WIKI_REQUEST_CACHE_MIDDLEWARE_CLASS = "openedx.core.djangoapps.request_cache.middleware.RequestCache" + # Settings for Comprehensive Theming app # See https://github.com/edx/edx-django-sites-extensions for more info diff --git a/openedx/core/djangoapps/bookmarks/services.py b/openedx/core/djangoapps/bookmarks/services.py index 8f4bcc78b4..4560040a03 100644 --- a/openedx/core/djangoapps/bookmarks/services.py +++ b/openedx/core/djangoapps/bookmarks/services.py @@ -4,8 +4,8 @@ Bookmarks service. import logging from django.core.exceptions import ObjectDoesNotExist -from edx_django_utils.cache import DEFAULT_REQUEST_CACHE +from openedx.core.djangoapps.request_cache.middleware import RequestCache from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -46,12 +46,12 @@ class BookmarksService(object): return [] cache_key = CACHE_KEY_TEMPLATE.format(self._user.id, course_key) - bookmarks_cache = DEFAULT_REQUEST_CACHE.data.get(cache_key, None) + bookmarks_cache = RequestCache.get_request_cache().data.get(cache_key, None) if bookmarks_cache is None and fetch is True: bookmarks_cache = api.get_bookmarks( self._user, course_key=course_key, fields=DEFAULT_FIELDS ) - DEFAULT_REQUEST_CACHE.data[cache_key] = bookmarks_cache + RequestCache.get_request_cache().data[cache_key] = bookmarks_cache return bookmarks_cache diff --git a/openedx/core/djangoapps/coursegraph/tasks.py b/openedx/core/djangoapps/coursegraph/tasks.py index 834ac1d4e5..093e4ef099 100644 --- a/openedx/core/djangoapps/coursegraph/tasks.py +++ b/openedx/core/djangoapps/coursegraph/tasks.py @@ -9,10 +9,10 @@ import logging from celery import task from django.conf import settings from django.utils import six, timezone -from edx_django_utils.cache import RequestCache from opaque_keys.edx.keys import CourseKey from py2neo import Graph, Node, Relationship, authenticate, NodeSelector from py2neo.compat import integer, string, unicode as neo4j_unicode +from openedx.core.djangoapps.request_cache.middleware import RequestCache log = logging.getLogger(__name__) @@ -348,7 +348,7 @@ class ModuleStoreSerializer(object): for index, course_key in enumerate(self.course_keys): # first, clear the request cache to prevent memory leaks - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() log.info( "Now submitting %s for export to neo4j: course %d of %d total courses", diff --git a/openedx/core/djangoapps/credit/models.py b/openedx/core/djangoapps/credit/models.py index bbc7f7e4cd..962efcf6aa 100644 --- a/openedx/core/djangoapps/credit/models.py +++ b/openedx/core/djangoapps/credit/models.py @@ -19,12 +19,12 @@ from django.db import IntegrityError, models, transaction from django.dispatch import receiver from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy -from edx_django_utils.cache import RequestCache from jsonfield.fields import JSONField from model_utils.models import TimeStampedModel from opaque_keys.edx.django.models import CourseKeyField -from openedx.core.djangoapps.request_cache.middleware import ns_request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, ns_request_cached +from student.models import get_retired_username_by_username CREDIT_PROVIDER_ID_REGEX = r"[a-z,A-Z,0-9,\-]+" log = logging.getLogger(__name__) @@ -401,7 +401,7 @@ class CreditRequirement(TimeStampedModel): @receiver(models.signals.post_delete, sender=CreditRequirement) def invalidate_credit_requirement_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of credit requirements. """ - RequestCache(namespace=CreditRequirement.CACHE_NAMESPACE).clear() + RequestCache.clear_request_cache(name=CreditRequirement.CACHE_NAMESPACE) class CreditRequirementStatus(TimeStampedModel): diff --git a/openedx/core/djangoapps/request_cache/__init__.py b/openedx/core/djangoapps/request_cache/__init__.py index 1e50ed5f3d..da444e521c 100644 --- a/openedx/core/djangoapps/request_cache/__init__.py +++ b/openedx/core/djangoapps/request_cache/__init__.py @@ -12,7 +12,6 @@ from celery.signals import task_postrun import crum from django.conf import settings from django.test.client import RequestFactory -from edx_django_utils.cache import RequestCache from openedx.core.djangoapps.request_cache import middleware @@ -27,7 +26,7 @@ def clear_request_cache(**kwargs): # pylint: disable=unused-argument prevent memory leaks. """ if getattr(settings, 'CLEAR_REQUEST_CACHE_ON_TASK_COMPLETION', True): - RequestCache.clear_all_namespaces() + middleware.RequestCache.clear_request_cache() def get_cache(name): @@ -39,8 +38,7 @@ def get_cache(name): Returns: dict """ - assert name is not None - return RequestCache(name).data + return middleware.RequestCache.get_request_cache(name) def clear_cache(name): @@ -50,7 +48,16 @@ def clear_cache(name): Arguments: name (str): The name of the request cache to clear """ - RequestCache(name).clear() + return middleware.RequestCache.clear_request_cache(name) + + +def get_request(): + """ + Return the current request. + + Deprecated: Please use crum to retrieve current requests. + """ + return crum.get_current_request() def get_request_or_stub(): diff --git a/openedx/core/djangoapps/request_cache/middleware.py b/openedx/core/djangoapps/request_cache/middleware.py index 90d5209098..525fb36f66 100644 --- a/openedx/core/djangoapps/request_cache/middleware.py +++ b/openedx/core/djangoapps/request_cache/middleware.py @@ -1,12 +1,67 @@ """ -The middleware for the edx-platform version of the RequestCache has been -removed in favor of the RequestCache found in edx-django-utils. +An implementation of a RequestCache. This cache is reset at the beginning +and end of every request. +""" +import threading -TODO: This file still contains request cache related decorators that -should be moved out of this middleware file. -""" +import crum from django.utils.encoding import force_text -from edx_django_utils.cache import RequestCache + + +class _RequestCache(threading.local): + """ + A thread-local for storing the per-request cache. + """ + def __init__(self): + super(_RequestCache, self).__init__() + self.data = {} + + +REQUEST_CACHE = _RequestCache() + + +class RequestCache(object): + @classmethod + def get_request_cache(cls, name=None): + """ + This method is deprecated. Please use :func:`request_cache.get_cache`. + """ + if name is None: + return REQUEST_CACHE + else: + return REQUEST_CACHE.data.setdefault(name, {}) + + @classmethod + def get_current_request(cls): + """ + This method is deprecated. Please use :func:`request_cache.get_request`. + """ + return crum.get_current_request() + + @classmethod + def clear_request_cache(cls, name=None): + """ + Empty the request cache. + """ + if name is None: + REQUEST_CACHE.data = {} + elif REQUEST_CACHE.data.get(name): + REQUEST_CACHE.data[name] = {} + + def process_request(self, request): + self.clear_request_cache() + return None + + def process_response(self, request, response): + self.clear_request_cache() + return response + + def process_exception(self, request, exception): # pylint: disable=unused-argument + """ + Clear the RequestCache after a failed request. + """ + self.clear_request_cache() + return None def request_cached(f): @@ -39,8 +94,7 @@ def ns_request_cached(namespace=None): Arguments: namespace (string): An optional namespace to use for the cache. Useful if the caller wants to manage - their own sub-cache by, for example, calling RequestCache(namespace=NAMESPACE).clear() for their own - namespace. + their own sub-cache by, for example, calling RequestCache.clear_request_cache for their own namespace. """ def outer_wrapper(f): """ @@ -55,22 +109,22 @@ def ns_request_cached(namespace=None): """ # Check to see if we have a result in cache. If not, invoke our wrapped # function. Cache and return the result to the caller. - request_cache = RequestCache(namespace) - cache_key = _func_call_cache_key(f, *args, **kwargs) + rcache = RequestCache.get_request_cache(namespace) + rcache = rcache.data if namespace is None else rcache + cache_key = func_call_cache_key(f, *args, **kwargs) - cached_response = request_cache.get_cached_response(cache_key) - if cached_response.is_found: - return cached_response.value - - result = f(*args, **kwargs) - request_cache.set(cache_key, result) - return result + if cache_key in rcache: + return rcache.get(cache_key) + else: + result = f(*args, **kwargs) + rcache[cache_key] = result + return result return inner_wrapper return outer_wrapper -def _func_call_cache_key(func, *args, **kwargs): +def func_call_cache_key(func, *args, **kwargs): """ Returns a cache key based on the function's module the function's name, and a stringified list of arguments diff --git a/openedx/core/djangoapps/request_cache/tests.py b/openedx/core/djangoapps/request_cache/tests.py index 68fc74faa6..c0c6223b18 100644 --- a/openedx/core/djangoapps/request_cache/tests.py +++ b/openedx/core/djangoapps/request_cache/tests.py @@ -6,17 +6,16 @@ from celery.task import task from django.conf import settings from django.test import TestCase from django.test.utils import override_settings -from edx_django_utils.cache import RequestCache from mock import Mock from openedx.core.djangoapps.request_cache import get_request_or_stub -from openedx.core.djangoapps.request_cache.middleware import request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, request_cached from xmodule.modulestore.django import modulestore class TestRequestCache(TestCase): """ - Tests for request cache helpers and decorators. + Tests for the request cache. """ def test_get_request_or_stub(self): @@ -44,7 +43,7 @@ class TestRequestCache(TestCase): """ Ensure that after a cache miss, we fill the cache and can hit it. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.return_value = 42 @@ -67,7 +66,7 @@ class TestRequestCache(TestCase): """ Ensure that after caching a result, we always send it back, even if the underlying result changes. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3] @@ -103,7 +102,7 @@ class TestRequestCache(TestCase): Ensure that calling a decorated function with different positional arguments will not use a cached value invoked by a previous call with different arguments. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6] @@ -144,7 +143,7 @@ class TestRequestCache(TestCase): Ensure that calling a decorated function with different keyword arguments will not use a cached value invoked by a previous call with different arguments. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [1, 2, 3, 4, 5, 6] @@ -189,7 +188,7 @@ class TestRequestCache(TestCase): """ Ensure that request_cached can work with mixed str and Unicode parameters. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() def dummy_function(arg1, arg2): """ @@ -213,7 +212,7 @@ class TestRequestCache(TestCase): properly caches the result and doesn't recall the underlying function. """ - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() to_be_wrapped = Mock() to_be_wrapped.side_effect = [None, None, None, 1, 1] diff --git a/openedx/core/djangoapps/theming/helpers.py b/openedx/core/djangoapps/theming/helpers.py index 9fcfa5bcf3..914c31aaec 100644 --- a/openedx/core/djangoapps/theming/helpers.py +++ b/openedx/core/djangoapps/theming/helpers.py @@ -8,7 +8,6 @@ import os import re from logging import getLogger -import crum from django.conf import settings from microsite_configuration import microsite @@ -20,7 +19,7 @@ from openedx.core.djangoapps.theming.helpers_dirs import ( get_theme_dirs, get_themes_unchecked ) -from openedx.core.djangoapps.request_cache.middleware import request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, request_cached logger = getLogger(__name__) # pylint: disable=invalid-name @@ -166,7 +165,7 @@ def get_current_request(): Returns: (HttpRequest): returns current request """ - return crum.get_current_request() + return RequestCache.get_current_request() def get_current_site(): diff --git a/openedx/core/djangoapps/theming/tests/test_helpers.py b/openedx/core/djangoapps/theming/tests/test_helpers.py index afde716822..570cfb670a 100644 --- a/openedx/core/djangoapps/theming/tests/test_helpers.py +++ b/openedx/core/djangoapps/theming/tests/test_helpers.py @@ -5,7 +5,6 @@ from mock import patch, Mock from django.test import TestCase, override_settings from django.conf import settings -from edx_django_utils.cache import RequestCache from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -13,6 +12,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers from openedx.core.djangoapps.theming.helpers import get_template_path_with_theme, strip_site_theme_templates_path, \ get_themes, Theme, get_theme_base_dir from openedx.core.djangolib.testing.utils import skip_unless_cms, skip_unless_lms +from openedx.core.djangoapps.request_cache.middleware import RequestCache class TestHelpers(TestCase): @@ -190,7 +190,7 @@ class TestHelpers(TestCase): mock_microsite_backend.get_template = Mock(return_value="/microsite/about.html") self.assertEqual(theming_helpers.get_template_path("about.html"), "about.html") - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() # if the current site does not have associated SiteTheme then get_template_path should return microsite override with patch( diff --git a/openedx/core/djangoapps/verified_track_content/models.py b/openedx/core/djangoapps/verified_track_content/models.py index d752ca7c0e..1d4136d065 100644 --- a/openedx/core/djangoapps/verified_track_content/models.py +++ b/openedx/core/djangoapps/verified_track_content/models.py @@ -8,7 +8,6 @@ from django.db import models from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from django.utils.translation import ugettext_lazy -from edx_django_utils.cache import RequestCache from opaque_keys.edx.django.models import CourseKeyField from lms.djangoapps.courseware.courses import get_course_by_id @@ -19,7 +18,7 @@ from openedx.core.djangoapps.course_groups.cohorts import ( is_course_cohorted ) from openedx.core.djangoapps.verified_track_content.tasks import sync_cohort_with_mode -from openedx.core.djangoapps.request_cache.middleware import ns_request_cached +from openedx.core.djangoapps.request_cache.middleware import RequestCache, ns_request_cached from student.models import CourseEnrollment log = logging.getLogger(__name__) @@ -148,7 +147,7 @@ class VerifiedTrackCohortedCourse(models.Model): @receiver(models.signals.post_delete, sender=VerifiedTrackCohortedCourse) def invalidate_verified_track_cache(sender, **kwargs): # pylint: disable=unused-argument """Invalidate the cache of VerifiedTrackCohortedCourse. """ - RequestCache(namespace=VerifiedTrackCohortedCourse.CACHE_NAMESPACE).clear() + RequestCache.clear_request_cache(name=VerifiedTrackCohortedCourse.CACHE_NAMESPACE) class MigrateVerifiedTrackCohortsSetting(ConfigurationModel): diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_init.py b/openedx/core/djangoapps/waffle_utils/tests/test_init.py index bcac166504..3facecca2e 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_init.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_init.py @@ -5,9 +5,9 @@ import crum import ddt from django.test import TestCase from django.test.client import RequestFactory -from edx_django_utils.cache import RequestCache from mock import patch from opaque_keys.edx.keys import CourseKey +from openedx.core.djangoapps.request_cache.middleware import RequestCache from waffle.testutils import override_flag from .. import CourseWaffleFlag, WaffleFlagNamespace, WaffleSwitchNamespace, WaffleSwitch @@ -34,7 +34,7 @@ class TestCourseWaffleFlag(TestCase): request = RequestFactory().request() self.addCleanup(crum.set_current_request, None) crum.set_current_request(request) - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() @ddt.data( {'course_override': WaffleFlagCourseOverrideModel.ALL_CHOICES.on, 'waffle_enabled': False, 'result': True}, diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_models.py b/openedx/core/djangoapps/waffle_utils/tests/test_models.py index b2aab23c82..5dbfbe0859 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_models.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_models.py @@ -3,9 +3,10 @@ Tests for waffle utils models. """ from ddt import data, ddt, unpack from django.test import TestCase -from edx_django_utils.cache import RequestCache from opaque_keys.edx.keys import CourseKey +from openedx.core.djangoapps.request_cache.middleware import RequestCache + from ..models import WaffleFlagCourseOverrideModel @@ -25,7 +26,7 @@ class WaffleFlagCourseOverrideTests(TestCase): (False, OVERRIDE_CHOICES.on, OVERRIDE_CHOICES.unset)) @unpack def test_setting_override(self, is_enabled, override_choice, expected_result): - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() self.set_waffle_course_override(override_choice, is_enabled) override_value = WaffleFlagCourseOverrideModel.override_value( self.WAFFLE_TEST_NAME, self.TEST_COURSE_KEY @@ -33,7 +34,7 @@ class WaffleFlagCourseOverrideTests(TestCase): self.assertEqual(override_value, expected_result) def test_setting_override_multiple_times(self): - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() self.set_waffle_course_override(self.OVERRIDE_CHOICES.on) self.set_waffle_course_override(self.OVERRIDE_CHOICES.off) override_value = WaffleFlagCourseOverrideModel.override_value( diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py b/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py index b9da185335..5c6f5e558d 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_testutils.py @@ -5,9 +5,9 @@ Tests for waffle utils test utilities. import crum from django.test import TestCase from django.test.client import RequestFactory -from edx_django_utils.cache import RequestCache from opaque_keys.edx.keys import CourseKey +from openedx.core.djangoapps.request_cache.middleware import RequestCache from .. import CourseWaffleFlag, WaffleFlagNamespace from ..testutils import override_waffle_flag @@ -30,7 +30,7 @@ class OverrideWaffleFlagTests(TestCase): request = RequestFactory().request() self.addCleanup(crum.set_current_request, None) crum.set_current_request(request) - RequestCache.clear_all_namespaces() + RequestCache.clear_request_cache() @override_waffle_flag(TEST_COURSE_FLAG, True) def assert_decorator_activates_flag(self): diff --git a/openedx/core/djangolib/testing/utils.py b/openedx/core/djangolib/testing/utils.py index a7c87de26a..f4359002c3 100644 --- a/openedx/core/djangolib/testing/utils.py +++ b/openedx/core/djangolib/testing/utils.py @@ -21,6 +21,8 @@ from django.test import RequestFactory, TestCase, override_settings from django.test.utils import CaptureQueriesContext from edx_django_utils.cache import RequestCache +from openedx.core.djangoapps.request_cache.middleware import RequestCache as DeprecatedRequestCache + class CacheIsolationMixin(object): """ @@ -118,6 +120,7 @@ class CacheIsolationMixin(object): # Clear that. sites.models.SITE_CACHE.clear() + DeprecatedRequestCache.clear_request_cache() RequestCache.clear_all_namespaces()