{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/56361177","id":56361177,"diff_hunk":"@@ -171,83 +255,84 @@ def default(self, block, name):\n         return self.fallback.default(block, name)\n \n \n-class _OverridesDisabled(threading.local):\n-    \"\"\"\n-    A thread local used to manage state of overrides being disabled or not.\n-    \"\"\"\n-    disabled = ()\n+class OverrideModulestoreFieldData(OverrideFieldData):\n+    \"\"\"Apply field data overrides at the modulestore level. No student context required.\"\"\"\n \n+    @classmethod\n+    def wrap(cls, block, field_data):  # pylint: disable=arguments-differ\n+        if cls.provider_classes is None:\n+            cls.provider_classes = [\n+                resolve_dotted(name) for name in settings.MODULESTORE_FIELD_OVERRIDE_PROVIDERS\n+            ]\n \n-_OVERRIDES_DISABLED = _OverridesDisabled()\n+        if block:\n+            enabled_providers = cls._providers_for_block(block)\n+            if enabled_providers:\n+                return cls(field_data, enabled_providers)\n \n+        return field_data\n \n-@contextmanager\n-def disable_overrides():\n-    \"\"\"\n-    A context manager which disables field overrides inside the context of a\n-    `with` statement, allowing code to get at the `original` value of a field.\n-    \"\"\"\n-    prev = _OVERRIDES_DISABLED.disabled\n-    _OVERRIDES_DISABLED.disabled += (True,)\n-    yield\n-    _OVERRIDES_DISABLED.disabled = prev\n+    @classmethod\n+    def _providers_for_block(cls, block):\n+        course_id = unicode(block.location.course_key)\n+        cache_key = ENABLED_MODULESTORE_OVERRIDE_PROVIDERS_KEY.format(course_id=course_id)\n \n+        request_cache = RequestCache.get_request_cache()\n+        enabled_providers = request_cache.data.get(cache_key)\n \n-def overrides_disabled():\n-    \"\"\"\n-    Checks to see whether overrides are disabled in the current context.\n-    Returns a boolean value.  See `disable_overrides`.\n-    \"\"\"\n-    return bool(_OVERRIDES_DISABLED.disabled)\n+        if enabled_providers is None:\n+            enabled_providers = [\n+                provider_class for provider_class in cls.provider_classes if provider_class.enabled_for(block)\n+            ]\n+            request_cache.data[cache_key] = enabled_providers\n \n+        return enabled_providers\n \n-class FieldOverrideProvider(object):\n-    \"\"\"\n-    Abstract class which defines the interface that a `FieldOverrideProvider`\n-    must provide.  In general, providers should derive from this class, but\n-    it's not strictly necessary as long as they correctly implement this\n-    interface.\n+    def __init__(self, fallback, providers):\n+        super(OverrideModulestoreFieldData, self).__init__(None, fallback, providers)\n \n-    A `FieldOverrideProvider` implementation is only responsible for looking up\n-    field overrides. To set overrides, there will be a domain specific API for\n-    the concrete override implementation being used.\n+\n+class FieldOverrideModulestoreWrapper(object):\n+    \"\"\"Apply field overrides to XBlocks.\n+\n+    Extends read-only functionality.\n     \"\"\"\n-    __metaclass__ = ABCMeta\n \n-    def __init__(self, user):\n-        self.user = user\n+    def __init__(self, modulestore):\n+        \"\"\"Wrap the provided modulestore\"\"\"\n+        self.__dict__['_modulestore'] = modulestore\n \n-    @abstractmethod\n-    def get(self, block, name, default):  # pragma no cover\n-        \"\"\"\n-        Look for an override value for the field named `name` in `block`.\n-        Returns the overridden value or `default` if no override is found.\n-        \"\"\"\n-        raise NotImplementedError\n+    def __getattr__(self, name):\n+        \"\"\"Look up missing attributes on the wrapped modulestore\"\"\"\n+        return getattr(self._modulestore, name)\n \n-    @abstractmethod\n-    def enabled_for(self, course):  # pragma no cover\n-        \"\"\"\n-        Return True if this provider should be enabled for a given course,\n-        and False otherwise.\n+    def __setattr__(self, name, value):\n+        \"\"\"Set attributes on the wrapped modulestore\"\"\"\n+        setattr(self._modulestore, name, value)\n \n-        Concrete implementations are responsible for implementing this method.\n+    def __delattr__(self, name):\n+        \"\"\"Delete attributes on the wrapped modulestore\"\"\"\n+        delattr(self._modulestore, name)\n \n-        Arguments:\n-          course (CourseModule or None)\n+    def _wrap(self, block):\n+        \"\"\"Apply XBlock field data overrides.\"\"\"\n+        block._field_data = OverrideModulestoreFieldData.wrap(block, block._field_data)  # pylint: disable=protected-access\n+        return block\n \n-        Returns:\n-          bool\n-        \"\"\"\n-        return False\n+    def get_item(self, usage_key, depth=0, **kwargs):\n+        return self._wrap(\n+            self._modulestore.get_item(usage_key, depth, **kwargs)\n+        )\n \n+    def get_items(self, course_key, **kwargs):\n+        items = [self._wrap(item) for item in self._modulestore.get_items(course_key, **kwargs)]\n+        return items\n \n-def _lineage(block):\n-    \"\"\"\n-    Returns an iterator over all ancestors of the given block, starting with\n-    its immediate parent and ending at the root of the block tree.\n-    \"\"\"\n-    parent = block.get_parent()\n-    while parent:\n-        yield parent\n-        parent = parent.get_parent()\n+    def get_course(self, course_key, depth=0, **kwargs):\n+        return self._wrap(\n+            self._modulestore.get_course(course_key, depth=depth, **kwargs)\n+        )\n+\n+    def get_courses(self, **kwargs):\n+        courses = [self._wrap(course) for course in self._modulestore.get_courses(**kwargs)]","path":"lms/djangoapps/courseware/field_overrides.py","position":252,"original_position":252,"commit_id":"5d4dbac4b3a72cc7fbbd2f13b0506566880e684d","original_commit_id":"5d4dbac4b3a72cc7fbbd2f13b0506566880e684d","user":{"login":"jimabramson","id":1349368,"avatar_url":"https://avatars.githubusercontent.com/u/1349368?v=3","gravatar_id":"","url":"https://api.github.com/users/jimabramson","html_url":"https://github.com/jimabramson","followers_url":"https://api.github.com/users/jimabramson/followers","following_url":"https://api.github.com/users/jimabramson/following{/other_user}","gists_url":"https://api.github.com/users/jimabramson/gists{/gist_id}","starred_url":"https://api.github.com/users/jimabramson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jimabramson/subscriptions","organizations_url":"https://api.github.com/users/jimabramson/orgs","repos_url":"https://api.github.com/users/jimabramson/repos","events_url":"https://api.github.com/users/jimabramson/events{/privacy}","received_events_url":"https://api.github.com/users/jimabramson/received_events","type":"User","site_admin":false},"body":"thanks, sorry, i put my glasses on wrong this morning.","created_at":"2016-03-16T16:01:27Z","updated_at":"2016-03-16T16:01:27Z","html_url":"https://github.com/edx/edx-platform/pull/11805#discussion_r56361177","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/11805","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/56361177"},"html":{"href":"https://github.com/edx/edx-platform/pull/11805#discussion_r56361177"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/11805"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/11805","id":62809680,"html_url":"https://github.com/edx/edx-platform/pull/11805","diff_url":"https://github.com/edx/edx-platform/pull/11805.diff","patch_url":"https://github.com/edx/edx-platform/pull/11805.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/11805","number":11805,"state":"open","locked":false,"title":"Add modulestore wrapper for applying XBlock field overrides","user":{"login":"rlucioni","id":969289,"avatar_url":"https://avatars.githubusercontent.com/u/969289?v=3","gravatar_id":"","url":"https://api.github.com/users/rlucioni","html_url":"https://github.com/rlucioni","followers_url":"https://api.github.com/users/rlucioni/followers","following_url":"https://api.github.com/users/rlucioni/following{/other_user}","gists_url":"https://api.github.com/users/rlucioni/gists{/gist_id}","starred_url":"https://api.github.com/users/rlucioni/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rlucioni/subscriptions","organizations_url":"https://api.github.com/users/rlucioni/orgs","repos_url":"https://api.github.com/users/rlucioni/repos","events_url":"https://api.github.com/users/rlucioni/events{/privacy}","received_events_url":"https://api.github.com/users/rlucioni/received_events","type":"User","site_admin":false},"body":"@schenedx @jimabramson @cpennington this is a proof-of-concept which resolves ECOM-3733 using a modulestore wrapper. I'm opening this PR to show you guys how I'm doing this and see how many tests I've broken. I've yet to add new tests for my changes.","created_at":"2016-03-14T20:12:22Z","updated_at":"2016-03-16T16:01:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2de70d906dbf4dc5f716b9efca2e2f9be5e1abf4","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/11805/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/11805/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/11805/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/5d4dbac4b3a72cc7fbbd2f13b0506566880e684d","head":{"label":"edx:renzo/self-paced-modulestore-wrapper","ref":"renzo/self-paced-modulestore-wrapper","sha":"5d4dbac4b3a72cc7fbbd2f13b0506566880e684d","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2016-03-16T11:36:14Z","pushed_at":"2016-03-16T15:37:37Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":744635,"stargazers_count":2446,"watchers_count":2446,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1485,"mirror_url":null,"open_issues_count":161,"forks":1485,"open_issues":161,"watchers":2446,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"b91f940af7e7d3bf089d451dc57f7f25e77d8879","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/edx/edx-platform/deployments","created_at":"2013-05-30T20:20:38Z","updated_at":"2016-03-16T11:36:14Z","pushed_at":"2016-03-16T15:37:37Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":744635,"stargazers_count":2446,"watchers_count":2446,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1485,"mirror_url":null,"open_issues_count":161,"forks":1485,"open_issues":161,"watchers":2446,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/11805"},"html":{"href":"https://github.com/edx/edx-platform/pull/11805"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/11805"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/11805/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/11805/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/11805/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/5d4dbac4b3a72cc7fbbd2f13b0506566880e684d"}}}}