diff --git a/common/lib/xmodule/xmodule/tests/test_import.py b/common/lib/xmodule/xmodule/tests/test_import.py
index cd7749dc09dfa7ff423dba1bead7356b55975ebd..19232e383fce00e41288f09c17ac92a56002ea51 100644
--- a/common/lib/xmodule/xmodule/tests/test_import.py
+++ b/common/lib/xmodule/xmodule/tests/test_import.py
@@ -91,7 +91,6 @@ class ImportTestCase(BaseCourseTestCase):
 
         self.assertNotEqual(descriptor1.location, descriptor2.location)
 
-    @unittest.skip('Temporarily disabled')
     def test_reimport(self):
         '''Make sure an already-exported error xml tag loads properly'''
 
diff --git a/common/lib/xmodule/xmodule/tests/test_xml_module.py b/common/lib/xmodule/xmodule/tests/test_xml_module.py
index 1515c76237d6b37be5460f91b1ceb77445d1593f..9d11b2b168f52827453ccc09cc3bd79f224b3f5c 100644
--- a/common/lib/xmodule/xmodule/tests/test_xml_module.py
+++ b/common/lib/xmodule/xmodule/tests/test_xml_module.py
@@ -1,17 +1,25 @@
 # disable missing docstring
 #pylint: disable=C0111
 
-from xmodule.x_module import XModuleFields
-from xblock.fields import Scope, String, Dict, Boolean, Integer, Float, Any, List
-from xblock.field_data import DictFieldData
-from xmodule.fields import Date, Timedelta
-from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field
 import unittest
-from nose.tools import assert_equals  # pylint: disable=E0611
+
 from mock import Mock
-from xmodule.modulestore.inheritance import InheritanceKeyValueStore, InheritanceMixin
+from nose.tools import assert_equals, assert_not_equals, assert_true, assert_false, assert_in, assert_not_in  # pylint: disable=E0611
+
+from xblock.field_data import DictFieldData
+from xblock.fields import Scope, String, Dict, Boolean, Integer, Float, Any, List
 from xblock.runtime import DbModel
+
+from xmodule.fields import Date, Timedelta
+from xmodule.modulestore.inheritance import InheritanceKeyValueStore, InheritanceMixin
+from xmodule.x_module import XModuleFields
+from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field
+from xmodule.course_module import CourseDescriptor
+from xmodule.seq_module import SequenceDescriptor
+
 from xmodule.tests import get_test_descriptor_system
+from xmodule.tests.xml import XModuleXmlImportTest
+from xmodule.tests.xml.factories import CourseFactory, SequenceFactory, ProblemFactory
 
 
 class CrazyJsonString(String):
@@ -379,3 +387,78 @@ class TestDeserializeTimedelta(TestDeserialize):
         self.assertDeserializeEqual('1 day 12 hours 59 minutes 59 seconds',
             '"1 day 12 hours 59 minutes 59 seconds"')
         self.assertDeserializeNonString()
+
+
+class TestXmlAttributes(XModuleXmlImportTest):
+
+    def test_unknown_attribute(self):
+        assert_false(hasattr(CourseDescriptor, 'unknown_attr'))
+        course = self.process_xml(CourseFactory.build(unknown_attr='value'))
+        assert_false(hasattr(course, 'unknown_attr'))
+        assert_equals('value', course.xml_attributes['unknown_attr'])
+
+    def test_known_attribute(self):
+        assert_true(hasattr(CourseDescriptor, 'show_chat'))
+        course = self.process_xml(CourseFactory.build(show_chat='true'))
+        assert_true(course.show_chat)
+        assert_not_in('show_chat', course.xml_attributes)
+
+    def test_rerandomize_in_policy(self):
+        # Rerandomize isn't a basic attribute of Sequence
+        assert_false(hasattr(SequenceDescriptor, 'rerandomize'))
+
+        root = SequenceFactory.build(policy={'rerandomize': 'never'})
+        ProblemFactory.build(parent=root)
+
+        seq = self.process_xml(root)
+
+        # Rerandomize is added to the constructed sequence via the InheritanceMixin
+        assert_equals('never', seq.rerandomize)
+
+        # Rerandomize is a known value coming from policy, and shouldn't appear
+        # in xml_attributes
+        assert_not_in('rerandomize', seq.xml_attributes)
+
+    def test_attempts_in_policy(self):
+        # attempts isn't a basic attribute of Sequence
+        assert_false(hasattr(SequenceDescriptor, 'attempts'))
+
+        root = SequenceFactory.build(policy={'attempts': '1'})
+        ProblemFactory.build(parent=root)
+
+        seq = self.process_xml(root)
+
+        # attempts isn't added to the constructed sequence, because
+        # it's not in the InheritanceMixin
+        assert_false(hasattr(seq, 'attempts'))
+
+        # attempts is an unknown attribute, so we should include it
+        # in xml_attributes so that it gets written out (despite the misleading
+        # name)
+        assert_in('attempts', seq.xml_attributes)
+
+    def test_inheritable_attribute(self):
+        # days_early_for_beta isn't a basic attribute of Sequence
+        assert_false(hasattr(SequenceDescriptor, 'days_early_for_beta'))
+
+        # days_early_for_beta is added by InheritanceMixin
+        assert_true(hasattr(InheritanceMixin, 'days_early_for_beta'))
+
+        root = SequenceFactory.build(policy={'days_early_for_beta': '2'})
+        ProblemFactory.build(parent=root)
+
+        # InheritanceMixin will be used when processing the XML
+        assert_in(InheritanceMixin, root.xblock_mixins)
+
+        seq = self.process_xml(root)
+
+        assert_equals(seq.unmixed_class, SequenceDescriptor)
+        assert_not_equals(type(seq), SequenceDescriptor)
+
+        # days_early_for_beta is added to the constructed sequence, because
+        # it's in the InheritanceMixin
+        assert_equals(2, seq.days_early_for_beta)
+
+        # days_early_for_beta is a known attribute, so we shouldn't include it
+        # in xml_attributes
+        assert_not_in('days_early_for_beta', seq.xml_attributes)
diff --git a/common/lib/xmodule/xmodule/tests/xml/__init__.py b/common/lib/xmodule/xmodule/tests/xml/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..32cefadca7252b0459b515abfde7d61c98bd8242
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/xml/__init__.py
@@ -0,0 +1,46 @@
+"""
+Xml parsing tests for XModules
+"""
+import pprint
+from mock import Mock
+
+from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
+from xmodule.mako_module import MakoDescriptorSystem
+
+
+class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem):  # pylint: disable=abstract-method
+    """
+    The simplest possible XMLParsingSystem
+    """
+    def __init__(self, xml_import_data):
+        self.org = xml_import_data.org
+        self.course = xml_import_data.course
+        self.default_class = xml_import_data.default_class
+        self._descriptors = {}
+        super(InMemorySystem, self).__init__(
+            policy=xml_import_data.policy,
+            process_xml=self.process_xml,
+            load_item=self.load_item,
+            error_tracker=Mock(),
+            resources_fs=xml_import_data.filesystem,
+            mixins=xml_import_data.xblock_mixins,
+            render_template=lambda template, context: pprint.pformat((template, context))
+        )
+
+    def process_xml(self, xml):  # pylint: disable=method-hidden
+        """Parse `xml` as an XModuleDescriptor, and add it to `self._descriptors`"""
+        descriptor = XModuleDescriptor.load_from_xml(xml, self, self.org, self.course, self.default_class)
+        self._descriptors[descriptor.location.url()] = descriptor
+        return descriptor
+
+    def load_item(self, location):  # pylint: disable=method-hidden
+        """Return the descriptor loaded for `location`"""
+        return self._descriptors[location]
+
+
+class XModuleXmlImportTest(object):
+    """Base class for tests that use basic `XModuleDescriptor.load_from_xml` xml parsing"""
+    def process_xml(self, xml_import_data):
+        """Use the `xml_import_data` to import an :class:`XModuleDescriptor` from xml"""
+        system = InMemorySystem(xml_import_data)
+        return system.process_xml(xml_import_data.xml_string)
diff --git a/common/lib/xmodule/xmodule/tests/xml/factories.py b/common/lib/xmodule/xmodule/tests/xml/factories.py
new file mode 100644
index 0000000000000000000000000000000000000000..168dcd579ba82f9d86f0337dd5df6374788f06c8
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/xml/factories.py
@@ -0,0 +1,128 @@
+"""
+Factories for generating edXML for testing XModule import
+"""
+
+import inspect
+
+from fs.memoryfs import MemoryFS
+from factory import Factory, lazy_attribute, post_generation, Sequence
+from lxml import etree
+
+from xmodule.modulestore.inheritance import InheritanceMixin
+
+
+class XmlImportData(object):
+    """
+    Class to capture all of the data needed to actually run an XML import,
+    so that the Factories have something to generate
+    """
+    def __init__(self, xml_node, xml=None, org=None, course=None,
+                 default_class=None, policy=None,
+                 filesystem=None, parent=None,
+                 xblock_mixins=()):
+
+        self._xml_node = xml_node
+        self._xml_string = xml
+        self.org = org
+        self.course = course
+        self.default_class = default_class
+        self.filesystem = filesystem
+        self.xblock_mixins = xblock_mixins
+        self.parent = parent
+
+        if policy is None:
+            self.policy = {}
+        else:
+            self.policy = policy
+
+    @property
+    def xml_string(self):
+        """Return the stringified version of the generated xml"""
+        if self._xml_string is not None:
+            return self._xml_string
+
+        return etree.tostring(self._xml_node)
+
+    def __repr__(self):
+        return u"XmlImportData{!r}".format((
+            self._xml_node, self._xml_string, self.org,
+            self.course, self.default_class, self.policy,
+            self.filesystem, self.parent, self.xblock_mixins
+        ))
+
+
+# Extract all argument names used to construct XmlImportData objects,
+# so that the factory doesn't treat them as XML attributes
+XML_IMPORT_ARGS = inspect.getargspec(XmlImportData.__init__).args
+
+
+class XmlImportFactory(Factory):
+    """
+    Factory for generating XmlImportData's, which can hold all the data needed
+    to run an XModule XML import
+    """
+    FACTORY_FOR = XmlImportData
+
+    filesystem = MemoryFS()
+    xblock_mixins = (InheritanceMixin,)
+    url_name = Sequence(str)
+    attribs = {}
+    policy = {}
+    tag = 'unknown'
+
+    @classmethod
+    def _adjust_kwargs(cls, **kwargs):
+        """
+        Adjust the kwargs to be passed to the generated class.
+
+        Any kwargs that match :fun:`XmlImportData.__init__` will be passed
+        through. Any other unknown `kwargs` will be treated as XML attributes
+
+        :param tag: xml tag for the generated :class:`Element` node
+        :param text: (Optional) specifies the text of the generated :class:`Element`.
+        :param policy: (Optional) specifies data for the policy json file for this node
+        :type policy: dict
+        :param attribs: (Optional) specify attributes for the XML node
+        :type attribs: dict
+        """
+        tag = kwargs.pop('tag', 'unknown')
+        kwargs['policy'] = {'{tag}/{url_name}'.format(tag=tag, url_name=kwargs['url_name']): kwargs['policy']}
+
+        kwargs['xml_node'].text = kwargs.pop('text', None)
+
+        kwargs['xml_node'].attrib.update(kwargs.pop('attribs', {}))
+        for key in kwargs.keys():
+            if key not in XML_IMPORT_ARGS:
+                kwargs['xml_node'].set(key, kwargs.pop(key))
+
+        return kwargs
+
+    @lazy_attribute
+    def xml_node(self):
+        """An :class:`xml.etree.Element`"""
+        return etree.Element(self.tag)
+
+    @post_generation
+    def parent(self, _create, extracted, **_):
+        """Hook to merge this xml into a parent xml node"""
+        if extracted is None:
+            return
+
+        extracted._xml_node.append(self._xml_node)  # pylint: disable=no-member, protected-access
+        extracted.policy.update(self.policy)
+
+
+class CourseFactory(XmlImportFactory):
+    """Factory for <course> nodes"""
+    tag = 'course'
+
+
+class SequenceFactory(XmlImportFactory):
+    """Factory for <sequential> nodes"""
+    tag = 'sequential'
+
+
+class ProblemFactory(XmlImportFactory):
+    """Factory for <problem> nodes"""
+    tag = 'problem'
+    text = '<h1>Empty Problem!</h1>'
diff --git a/common/lib/xmodule/xmodule/tests/xml/test_inheritance.py b/common/lib/xmodule/xmodule/tests/xml/test_inheritance.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc27f0c7920cd416c5b1e5af57cc26ab76a8bd84
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/xml/test_inheritance.py
@@ -0,0 +1,29 @@
+"""
+Test that inherited fields work correctly when parsing XML
+"""
+from nose.tools import assert_equals  # pylint: disable=no-name-in-module
+
+from xmodule.tests.xml import XModuleXmlImportTest
+from xmodule.tests.xml.factories import CourseFactory, SequenceFactory, ProblemFactory
+
+
+class TestInheritedFieldParsing(XModuleXmlImportTest):
+    """
+    Test that inherited fields work correctly when parsing XML
+
+    """
+    def test_null_string(self):
+        # Test that the string inherited fields are passed through 'deserialize_field',
+        # which converts the string "null" to the python value None
+        root = CourseFactory.build(days_early_for_beta="null")
+        sequence = SequenceFactory.build(parent=root)
+        ProblemFactory.build(parent=sequence)
+
+        course = self.process_xml(root)
+        assert_equals(None, course.days_early_for_beta)
+
+        sequence = course.get_children()[0]
+        assert_equals(None, sequence.days_early_for_beta)
+
+        problem = sequence.get_children()[0]
+        assert_equals(None, problem.days_early_for_beta)
diff --git a/common/lib/xmodule/xmodule/tests/xml/test_policy.py b/common/lib/xmodule/xmodule/tests/xml/test_policy.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e702cc82d1f24fb2a7a69641e91c0daf22b07d9
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/xml/test_policy.py
@@ -0,0 +1,30 @@
+"""
+Tests that policy json files import correctly when loading XML
+"""
+
+from nose.tools import assert_equals, assert_raises  # pylint: disable=no-name-in-module
+
+from xmodule.tests.xml.factories import CourseFactory
+from xmodule.tests.xml import XModuleXmlImportTest
+
+
+class TestPolicy(XModuleXmlImportTest):
+    """
+    Tests that policy json files import correctly when loading xml
+    """
+    def test_no_attribute_mapping(self):
+        # Policy files are json, and thus the values aren't passed through 'deserialize_field'
+        # Therefor, the string 'null' is passed unchanged to the Float field, which will trigger
+        # a ValueError
+        with assert_raises(ValueError):
+            course = self.process_xml(CourseFactory.build(policy={'days_early_for_beta': 'null'}))
+
+            # Trigger the exception by looking at the imported data
+            course.days_early_for_beta  # pylint: disable=pointless-statement
+
+    def test_course_policy(self):
+        course = self.process_xml(CourseFactory.build(policy={'days_early_for_beta': None}))
+        assert_equals(None, course.days_early_for_beta)
+
+        course = self.process_xml(CourseFactory.build(policy={'days_early_for_beta': 9}))
+        assert_equals(9, course.days_early_for_beta)
diff --git a/common/lib/xmodule/xmodule/video_module.py b/common/lib/xmodule/xmodule/video_module.py
index d97d5aa144acb4d880720951e80bea31d1946ee1..febfe5961f8046aabac56ae6d8cbc3c17e139972 100644
--- a/common/lib/xmodule/xmodule/video_module.py
+++ b/common/lib/xmodule/xmodule/video_module.py
@@ -24,7 +24,7 @@ from django.conf import settings
 from xmodule.x_module import XModule
 from xmodule.editing_module import TabsEditingDescriptor
 from xmodule.raw_module import EmptyDataRawDescriptor
-from xmodule.xml_module import is_pointer_tag, name_to_pathname
+from xmodule.xml_module import is_pointer_tag, name_to_pathname, deserialize_field
 from xmodule.modulestore import Location
 from xblock.fields import Scope, String, Boolean, Float, List, Integer, ScopeIds
 
@@ -217,7 +217,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
         # For backwards compatibility -- if we've got XML data, parse
         # it out and set the metadata fields
         if self.data:
-            field_data = VideoDescriptor._parse_video_xml(self.data)
+            field_data = self._parse_video_xml(self.data)
             self._field_data.set_many(self, field_data)
             del self.data
 
@@ -241,7 +241,7 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
         if is_pointer_tag(xml_object):
             filepath = cls._format_filepath(xml_object.tag, name_to_pathname(url_name))
             xml_data = etree.tostring(cls.load_file(filepath, system.resources_fs, location))
-        field_data = VideoDescriptor._parse_video_xml(xml_data)
+        field_data = cls._parse_video_xml(xml_data)
         field_data['location'] = location
         kvs = InheritanceKeyValueStore(initial_values=field_data)
         field_data = DbModel(kvs)
@@ -292,8 +292,8 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
             xml.append(ele)
         return xml
 
-    @staticmethod
-    def _parse_youtube(data):
+    @classmethod
+    def _parse_youtube(cls, data):
         """
         Parses a string of Youtube IDs such as "1.0:AXdE34_U,1.5:VO3SxfeD"
         into a dictionary. Necessary for backwards compatibility with
@@ -310,14 +310,14 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
                 # Handle the fact that youtube IDs got double-quoted for a period of time.
                 # Note: we pass in "VideoFields.youtube_id_1_0" so we deserialize as a String--
                 # it doesn't matter what the actual speed is for the purposes of deserializing.
-                youtube_id = VideoDescriptor._deserialize(VideoFields.youtube_id_1_0.name, pieces[1])
+                youtube_id = deserialize_field(cls.youtube_id_1_0, pieces[1])
                 ret[speed] = youtube_id
             except (ValueError, IndexError):
                 log.warning('Invalid YouTube ID: %s' % video)
         return ret
 
-    @staticmethod
-    def _parse_video_xml(xml_data):
+    @classmethod
+    def _parse_video_xml(cls, xml_data):
         """
         Parse video fields out of xml_data. The fields are set if they are
         present in the XML.
@@ -326,8 +326,8 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
         field_data = {}
 
         conversions = {
-            'start_time': VideoDescriptor._parse_time,
-            'end_time': VideoDescriptor._parse_time
+            'start_time': cls._parse_time,
+            'end_time': cls._parse_time
         }
 
         # Convert between key names for certain attributes --
@@ -349,10 +349,10 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
         for attr, value in xml.items():
             if attr in compat_keys:
                 attr = compat_keys[attr]
-            if attr in VideoDescriptor.metadata_to_strip + ('url_name', 'name'):
+            if attr in cls.metadata_to_strip + ('url_name', 'name'):
                 continue
             if attr == 'youtube':
-                speeds = VideoDescriptor._parse_youtube(value)
+                speeds = cls._parse_youtube(value)
                 for speed, youtube_id in speeds.items():
                     # should have made these youtube_id_1_00 for
                     # cleanliness, but hindsight doesn't need glasses
@@ -367,20 +367,13 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
                 else:
                 # We export values with json.dumps (well, except for Strings, but
                 # for about a month we did it for Strings also).
-                    value = VideoDescriptor._deserialize(attr, value)
+                    value = deserialize_field(cls.fields[attr], value)
                 field_data[attr] = value
 
         return field_data
 
     @classmethod
-    def _deserialize(cls, attr, value):
-        """
-        Handles deserializing values that may have been encoded with json.dumps.
-        """
-        return cls.get_map_for_field(attr).from_xml(value)
-
-    @staticmethod
-    def _parse_time(str_time):
+    def _parse_time(cls, str_time):
         """Converts s in '12:34:45' format to seconds. If s is
         None, returns empty string"""
         if not str_time:
diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py
index 5fa9ff3260feeb3c109ceacc90116697c7a53cfa..83bb6faffdc6a5759b01a60c6f4d9b8b36ef3263 100644
--- a/common/lib/xmodule/xmodule/xml_module.py
+++ b/common/lib/xmodule/xmodule/xml_module.py
@@ -62,23 +62,6 @@ def get_metadata_from_xml(xml_object, remove=True):
         xml_object.remove(meta)
     return dmdata
 
-_AttrMapBase = namedtuple('_AttrMap', 'from_xml to_xml')
-
-
-class AttrMap(_AttrMapBase):
-    """
-    A class that specifies two functions:
-
-        from_xml: convert value from the xml representation into
-            an internal python representation
-
-        to_xml: convert the internal python representation into
-            the value to store in the xml.
-    """
-    def __new__(_cls, from_xml=lambda x: x,
-                to_xml=lambda x: x):
-        return _AttrMapBase.__new__(_cls, from_xml, to_xml)
-
 
 def serialize_field(value):
     """
@@ -166,20 +149,6 @@ class XmlDescriptor(XModuleDescriptor):
 
     metadata_to_export_to_policy = ('discussion_topics', 'checklists')
 
-    @classmethod
-    def get_map_for_field(cls, attr):
-        """
-        Returns a serialize/deserialize AttrMap for the given field of a class.
-
-        Searches through fields defined by cls to find one named attr.
-        """
-        if attr in cls.fields:
-            from_xml = lambda val: deserialize_field(cls.fields[attr], val)
-            to_xml = lambda val: serialize_field(val)
-            return AttrMap(from_xml, to_xml)
-        else:
-            return AttrMap()
-
     @classmethod
     def definition_from_xml(cls, xml_object, system):
         """
@@ -274,19 +243,19 @@ class XmlDescriptor(XModuleDescriptor):
 
         Returns a dictionary {key: value}.
         """
-        metadata = {}
-        for attr in xml_object.attrib:
-            val = xml_object.get(attr)
-            if val is not None:
-                # VS[compat].  Remove after all key translations done
-                attr = cls._translate(attr)
-
-                if attr in cls.metadata_to_strip:
-                    # don't load these
-                    continue
-
-                attr_map = cls.get_map_for_field(attr)
-                metadata[attr] = attr_map.from_xml(val)
+        metadata = {'xml_attributes': {}}
+        for attr, val in xml_object.attrib.iteritems():
+            # VS[compat].  Remove after all key translations done
+            attr = cls._translate(attr)
+
+            if attr in cls.metadata_to_strip:
+                # don't load these
+                continue
+
+            if attr not in cls.fields:
+                metadata['xml_attributes'][attr] = val
+            else:
+                metadata[attr] = deserialize_field(cls.fields[attr], val)
         return metadata
 
     @classmethod
@@ -295,9 +264,14 @@ class XmlDescriptor(XModuleDescriptor):
         Add the keys in policy to metadata, after processing them
         through the attrmap.  Updates the metadata dict in place.
         """
-        for attr in policy:
-            attr_map = cls.get_map_for_field(attr)
-            metadata[cls._translate(attr)] = attr_map.from_xml(policy[attr])
+        for attr, value in policy.iteritems():
+            attr = cls._translate(attr)
+            if attr not in cls.fields:
+                # Store unknown attributes coming from policy.json
+                # in such a way that they will export to xml unchanged
+                metadata['xml_attributes'][attr] = value
+            else:
+                metadata[attr] = value
 
     @classmethod
     def from_xml(cls, xml_data, system, org=None, course=None):
@@ -357,11 +331,7 @@ class XmlDescriptor(XModuleDescriptor):
         field_data.update(definition)
         field_data['children'] = children
 
-        field_data['xml_attributes'] = {}
         field_data['xml_attributes']['filename'] = definition.get('filename', ['', None])  # for git link
-        for key, value in metadata.items():
-            if key not in cls.fields:
-                field_data['xml_attributes'][key] = value
         field_data['location'] = location
         field_data['category'] = xml_object.tag
         kvs = InheritanceKeyValueStore(initial_values=field_data)
@@ -415,18 +385,11 @@ class XmlDescriptor(XModuleDescriptor):
         # Set the tag so we get the file path right
         xml_object.tag = self.category
 
-        def val_for_xml(attr):
-            """Get the value for this attribute that we want to store.
-            (Possible format conversion through an AttrMap).
-             """
-            attr_map = self.get_map_for_field(attr)
-            return attr_map.to_xml(self._field_data.get(self, attr))
-
         # Add the non-inherited metadata
         for attr in sorted(own_metadata(self)):
             # don't want e.g. data_dir
             if attr not in self.metadata_to_strip and attr not in self.metadata_to_export_to_policy:
-                val = val_for_xml(attr)
+                val = serialize_field(self._field_data.get(self, attr))
                 try:
                     xml_object.set(attr, val)
                 except Exception, e:
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py
index acdd4f0aa0cd78aa0a821b64c7b3b784ea8d33a3..f86bef29adea8fe2797021247587b3a6ae6aef3c 100644
--- a/lms/djangoapps/courseware/module_render.py
+++ b/lms/djangoapps/courseware/module_render.py
@@ -306,7 +306,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
         # Construct the key for the module
         key = KeyValueStore.Key(
             scope=Scope.user_state,
-            student_id=user.id,
+            user_id=user.id,
             block_scope_id=descriptor.location,
             field_name='grade'
         )
diff --git a/pylintrc b/pylintrc
index 9525f04362ee9349d4c6c2b9068b3cd64b8eed85..a3c84c15558695ece12d81df2dd7a0b97887d4cc 100644
--- a/pylintrc
+++ b/pylintrc
@@ -89,6 +89,9 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme
 # evaluation report (RP0004).
 comment=no
 
+# Display symbolic names of messages in reports
+symbols=yes
+
 
 [TYPECHECK]
 
@@ -120,7 +123,10 @@ generated-members=
     content,
     status_code,
 # For factory_boy factories
-    create
+    create,
+    build,
+# For xblocks
+    fields,
 
 
 [BASIC]
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index addef7b629cce74920ef05f3ec8ac07794a8f3ab..52fe3ce44f0a23d56337f8caef1406df753b2309 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -47,7 +47,7 @@ Pillow==1.7.8
 pip>=1.4
 polib==1.0.3
 pycrypto>=2.6
-pygments==1.5
+pygments==1.6
 pygraphviz==1.1
 pymongo==2.4.1
 pyparsing==1.5.6
diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt
index add9bda1d1c9c50f263c96e9db0f9925a82ebf53..e0c88b217e860a232b9f21b2b45d868e2ce1e11f 100644
--- a/requirements/edx/github.txt
+++ b/requirements/edx/github.txt
@@ -14,8 +14,8 @@
 -e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
 
 # Our libraries:
--e git+https://github.com/edx/XBlock.git@aa0d60627#egg=XBlock
+-e git+https://github.com/edx/XBlock.git@a8de02c0#egg=XBlock
 -e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail
--e git+https://github.com/edx/diff-cover.git@v0.2.3#egg=diff_cover
+-e git+https://github.com/edx/diff-cover.git@v0.2.4#egg=diff_cover
 -e git+https://github.com/edx/js-test-tool.git@v0.0.7#egg=js_test_tool
 -e git+https://github.com/edx/django-waffle.git@823a102e48#egg=django-waffle