prevent overly nesting _field_data (TNL-2050)

This commit is contained in:
Adam Palay
2015-05-01 17:09:18 -04:00
parent 0866f056c8
commit afec81b662
10 changed files with 137 additions and 24 deletions

View File

@@ -15,6 +15,7 @@ from django.test import TestCase
from django.test.utils import override_settings
from request_cache.middleware import RequestCache
from courseware.field_overrides import OverrideFieldData # pylint: disable=import-error
from xmodule.contentstore.django import _CONTENTSTORE
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore, clear_existing_modulestores
@@ -261,6 +262,11 @@ class ModuleStoreTestCase(TestCase):
self.addCleanup(XMODULE_FACTORY_LOCK.disable)
XMODULE_FACTORY_LOCK.enable()
# When testing CCX, we should make sure that
# OverrideFieldData.provider_classes is always reset to `None` so
# that they're recalculated for every test
OverrideFieldData.provider_classes = None
super(ModuleStoreTestCase, self).setUp()
self.store = modulestore()

View File

@@ -14,6 +14,7 @@ from importlib import import_module
from lxml import etree
from path import path
from contextlib import contextmanager
from lazy import lazy
from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import make_error_tracker, exc_info_to_str
@@ -933,6 +934,9 @@ class LibraryXMLModuleStore(XMLModuleStore):
here manually.
"""
init_dict = {key: getattr(library_descriptor, key) for key in library_descriptor.fields.keys()}
# if set, invalidate '_unwrapped_field_data' so it will be reset
# the next time it will be called
lazy.invalidate(library_descriptor, '_unwrapped_field_data')
# pylint: disable=protected-access
library_descriptor._field_data = inheriting_field_data(InheritanceKeyValueStore(init_dict))

View File

@@ -126,7 +126,7 @@ def get_test_system(course_id=SlashSeparatedCourseKey('org', 'course', 'run')):
# So, bind to the same one as the current descriptor.
module_system.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access
descriptor.bind_for_student(module_system, descriptor._field_data, user.id)
descriptor.bind_for_student(module_system, user.id)
return descriptor

View File

@@ -281,7 +281,13 @@ class ImportTestCase(BaseCourseTestCase):
due=from_date_string, org=ORG, course=COURSE, url_name=url_name, unicorn_color=unicorn_color
)
descriptor = system.process_xml(start_xml)
# pylint: disable=protected-access
original_unwrapped = descriptor._unwrapped_field_data
LibraryXMLModuleStore.patch_descriptor_kvs(descriptor)
# '_unwrapped_field_data' is reset in `patch_descriptor_kvs`
# pylint: disable=protected-access
self.assertIsNot(original_unwrapped, descriptor._unwrapped_field_data)
compute_inherited_metadata(descriptor)
# Check the course module, since it has inheritance
descriptor = descriptor.get_children()[0]

View File

@@ -60,7 +60,7 @@ class LibraryContentTest(MixedSplitTestCase):
sub_module_system = get_test_system(course_id=module.location.course_key)
sub_module_system.get_module = get_module
sub_module_system.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access
descriptor.bind_for_student(sub_module_system, descriptor._field_data, self.user_id) # pylint: disable=protected-access
descriptor.bind_for_student(sub_module_system, self.user_id)
return descriptor
module_system.get_module = get_module

View File

@@ -100,7 +100,6 @@ class SplitTestModuleTest(XModuleXmlImportTest, PartitionTestCase):
self.split_test_module = self.course_sequence.get_children()[0]
self.split_test_module.bind_for_student(
self.module_system,
self.split_test_module._field_data, # pylint: disable=protected-access
user.id
)

View File

@@ -16,6 +16,7 @@ from pkg_resources import (
)
from webob import Response
from webob.multidict import MultiDict
from lazy import lazy
from xblock.core import XBlock, XBlockAside
from xblock.fields import (
@@ -361,6 +362,14 @@ class XModuleMixin(XModuleFields, XBlockMixin):
self.save()
return self._field_data._kvs # pylint: disable=protected-access
@lazy
def _unwrapped_field_data(self):
"""
This property hold the value _field_data here before we wrap it in
the LmsFieldData or OverrideFieldData classes.
"""
return self._field_data
def get_explicitly_set_fields_by_scope(self, scope=Scope.content):
"""
Get a dictionary of the fields for the given scope which are set explicitly on this xblock. (Including
@@ -542,14 +551,17 @@ class XModuleMixin(XModuleFields, XBlockMixin):
"""
return None
def bind_for_student(self, xmodule_runtime, field_data, user_id):
def bind_for_student(self, xmodule_runtime, user_id, wrappers=None):
"""
Set up this XBlock to act as an XModule instead of an XModuleDescriptor.
Arguments:
xmodule_runtime (:class:`ModuleSystem'): the runtime to use when accessing student facing methods
field_data (:class:`FieldData`): The :class:`FieldData` to use for all subsequent data access
user_id: The user_id to set in scope_ids
wrappers: These are a list functions that put a wrapper, such as
LmsFieldData or OverrideFieldData, around the field_data.
Note that the functions will be applied in the order in
which they're listed. So [f1, f2] -> f2(f1(field_data))
"""
# pylint: disable=attribute-defined-outside-init
@@ -578,7 +590,15 @@ class XModuleMixin(XModuleFields, XBlockMixin):
# Set the new xmodule_runtime and field_data (which are user-specific)
self.xmodule_runtime = xmodule_runtime
self._field_data = field_data
if wrappers is None:
wrappers = []
wrapped_field_data = self._unwrapped_field_data
for wrapper in wrappers:
wrapped_field_data = wrapper(wrapped_field_data)
self._field_data = wrapped_field_data
@property
def non_editable_metadata_fields(self):
@@ -1237,7 +1257,7 @@ class MetricsMixin(object):
)
class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method
class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method
"""
Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s
"""
@@ -1483,7 +1503,7 @@ class XMLParsingSystem(DescriptorSystem):
setattr(xblock, field.name, field_value)
class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method
class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method
"""
This is an abstraction such that x_modules can function independent
of the courseware (e.g. import into other types of courseware, LMS,