Commit ef6da22a by Victor Shnayder

Add a fallback MalformedDescriptor

* when things don't load normally, use this
* separate raw editing functionality into EditingDescriptor
* raw descriptor just enforces that xml is valid
* add a MalformedDescriptor that just saves a string
* Fallback to it on import.
parent 4e6a9b0d
from path import path
import unittest
from xmodule.x_module import XMLParsingSystem, XModuleDescriptor
from xmodule.errorhandlers import ignore_errors_handler
from xmodule.modulestore import Location
class ImportTestCase(unittest.TestCase):
'''Make sure module imports work properly, including for malformed inputs'''
def test_fallback(self):
'''Make sure that malformed xml loads as a MalformedDescriptorb.'''
bad_xml = '''<sequential display_name="oops"><video url="hi"></sequential>'''
# Shouldn't need any system params, because the initial parse should fail
def load_item(loc):
raise Exception("Shouldn't be called")
resources_fs = None
def process_xml(xml):
raise Exception("Shouldn't be called")
def render_template(template, context):
raise Exception("Shouldn't be called")
system = XMLParsingSystem(load_item, resources_fs,
ignore_errors_handler, process_xml)
system.render_template = render_template
descriptor = XModuleDescriptor.load_from_xml(bad_xml, system, 'org', 'course',
None)
self.assertEqual(descriptor.__class__.__name__,
'MalformedDescriptor')
from pkg_resources import resource_string
from lxml import etree
from xmodule.mako_module import MakoModuleDescriptor
import logging
log = logging.getLogger(__name__)
class EditingDescriptor(MakoModuleDescriptor):
"""
Module that provides a raw editing view of its data and children. It does not
perform any validation on its definition---just passes it along to the browser.
This class is intended to be used as a mixin.
"""
mako_template = "widgets/raw-edit.html"
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]}
js_module_name = "RawDescriptor"
def get_context(self):
return {
'module': self,
'data': self.definition['data'],
}
from pkg_resources import resource_string
from lxml import etree
from xmodule.mako_module import MakoModuleDescriptor
from xmodule.xml_module import XmlDescriptor
from xmodule.editing_module import EditingDescriptor
import logging
log = logging.getLogger(__name__)
class MalformedDescriptor(EditingDescriptor):
"""
Module that provides a raw editing view of broken xml.
"""
@classmethod
def from_xml(cls, xml_data, system, org=None, course=None):
'''Create an instance of this descriptor from the supplied data.
Does not try to parse the data--just stores it.
'''
# TODO (vshnayder): how does one get back from this to a valid descriptor?
# try to parse and if successfull, send back to x_module?
definition = { 'data' : xml_data }
# TODO (vshnayder): Do we need a valid slug here? Just pick a random
# 64-bit num?
location = ['i4x', org, course, 'malformed', 'slug']
metadata = {} # stays in the xml_data
return cls(system, definition, location=location, metadata=metadata)
def export_to_xml(self, resource_fs):
'''
Export as a string wrapped in xml
'''
root = etree.Element('malformed')
root.text = self.definition['data']
return etree.tostring(root)
from pkg_resources import resource_string from pkg_resources import resource_string
from lxml import etree from lxml import etree
from xmodule.editing_module import EditingDescriptor
from xmodule.mako_module import MakoModuleDescriptor from xmodule.mako_module import MakoModuleDescriptor
from xmodule.xml_module import XmlDescriptor from xmodule.xml_module import XmlDescriptor
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class RawDescriptor(XmlDescriptor, EditingDescriptor):
class RawDescriptor(MakoModuleDescriptor, XmlDescriptor):
""" """
Module that provides a raw editing view of its data and children Module that provides a raw editing view of its data and children. It
requires that the definition xml is valid.
""" """
mako_template = "widgets/raw-edit.html"
js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]}
js_module_name = "RawDescriptor"
def get_context(self):
return {
'module': self,
'data': self.definition['data'],
}
@classmethod @classmethod
def definition_from_xml(cls, xml_object, system): def definition_from_xml(cls, xml_object, system):
return {'data': etree.tostring(xml_object)} return {'data': etree.tostring(xml_object)}
......
from lxml import etree from lxml import etree
from lxml.etree import XMLSyntaxError
import pkg_resources import pkg_resources
import logging import logging
from fs.errors import ResourceNotFoundError
from functools import partial
from xmodule.modulestore import Location from xmodule.modulestore import Location
from functools import partial
log = logging.getLogger('mitx.' + __name__) log = logging.getLogger('mitx.' + __name__)
...@@ -443,16 +445,28 @@ class XModuleDescriptor(Plugin, HTMLSnippet): ...@@ -443,16 +445,28 @@ class XModuleDescriptor(Plugin, HTMLSnippet):
system is an XMLParsingSystem system is an XMLParsingSystem
org and course are optional strings that will be used in the generated org and course are optional strings that will be used in the generated
modules url identifiers module's url identifiers
""" """
class_ = XModuleDescriptor.load_class( try:
etree.fromstring(xml_data).tag, class_ = XModuleDescriptor.load_class(
default_class etree.fromstring(xml_data).tag,
) default_class
# leave next line, commented out - useful for low-level debugging )
# log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % ( # leave next line, commented out - useful for low-level debugging
# etree.fromstring(xml_data).tag,class_)) # log.debug('[XModuleDescriptor.load_from_xml] tag=%s, class_=%s' % (
return class_.from_xml(xml_data, system, org, course) # etree.fromstring(xml_data).tag,class_))
descriptor = class_.from_xml(xml_data, system, org, course)
except (ResourceNotFoundError, XMLSyntaxError) as err:
# Didn't load properly. Fall back on loading as a malformed
# descriptor. This should never error due to formatting.
# Put import here to avoid circular import errors
from xmodule.malformed_module import MalformedDescriptor
descriptor = MalformedDescriptor.from_xml(xml_data, system, org, course)
return descriptor
@classmethod @classmethod
def from_xml(cls, xml_data, system, org=None, course=None): def from_xml(cls, xml_data, system, org=None, course=None):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment