diff --git a/common/lib/xmodule/xmodule/backcompat_module.py b/common/lib/xmodule/xmodule/backcompat_module.py index d379ced507..c040eca398 100644 --- a/common/lib/xmodule/xmodule/backcompat_module.py +++ b/common/lib/xmodule/xmodule/backcompat_module.py @@ -12,8 +12,8 @@ log = logging.getLogger(__name__) def process_includes(fn): """ Wraps a XModuleDescriptor.from_xml method, and modifies xml_data to replace - any immediate child items with the contents of the file that they are - supposed to include + any immediate child items with the contents of the file that they + are supposed to include """ @wraps(fn) def from_xml(cls, xml_data, system, org=None, course=None): @@ -25,15 +25,19 @@ def process_includes(fn): try: ifp = system.resources_fs.open(file) except Exception: - log.exception('Error in problem xml include: %s' % (etree.tostring(next_include, pretty_print=True))) - log.exception('Cannot find file %s in %s' % (file, dir)) + msg = 'Error in problem xml include: %s\n' % ( + etree.tostring(next_include, pretty_print=True)) + msg += 'Cannot find file %s in %s' % (file, dir) + log.exception(msg) raise try: # read in and convert to XML incxml = etree.XML(ifp.read()) except Exception: - log.exception('Error in problem xml include: %s' % (etree.tostring(next_include, pretty_print=True))) - log.exception('Cannot parse XML in %s' % (file)) + msg = 'Error in problem xml include: %s\n' % ( + etree.tostring(next_include, pretty_print=True)) + msg += 'Cannot parse XML in %s' % (file) + log.exception(msg) raise # insert new XML into tree in place of inlcude parent = next_include.getparent() @@ -50,8 +54,8 @@ class SemanticSectionDescriptor(XModuleDescriptor): @process_includes def from_xml(cls, xml_data, system, org=None, course=None): """ - Removes sections single child elements in favor of just embedding the child element - + Removes sections with single child elements in favor of just embedding + the child element """ xml_object = etree.fromstring(xml_data) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 263e062887..5ff0c13198 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -67,7 +67,8 @@ class ComplexEncoder(json.JSONEncoder): class CapaModule(XModule): ''' - An XModule implementing LonCapa format problems, implemented by way of capa.capa_problem.LoncapaProblem + An XModule implementing LonCapa format problems, implemented by way of + capa.capa_problem.LoncapaProblem ''' icon_class = 'problem' @@ -77,8 +78,10 @@ class CapaModule(XModule): js_module_name = "Problem" css = {'scss': [resource_string(__name__, 'css/capa/display.scss')]} - def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): - XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) + def __init__(self, system, location, definition, instance_state=None, + shared_state=None, **kwargs): + XModule.__init__(self, system, location, definition, instance_state, + shared_state, **kwargs) self.attempts = 0 self.max_attempts = None @@ -133,7 +136,8 @@ class CapaModule(XModule): seed = None try: - self.lcp = LoncapaProblem(self.definition['data'], self.location.html_id(), instance_state, seed=seed, system=self.system) + self.lcp = LoncapaProblem(self.definition['data'], self.location.html_id(), + instance_state, seed=seed, system=self.system) except Exception: msg = 'cannot create LoncapaProblem %s' % self.location.url() log.exception(msg) @@ -141,15 +145,20 @@ class CapaModule(XModule): msg = '

%s

' % msg.replace('<', '<') msg += '

%s

' % traceback.format_exc().replace('<', '<') # create a dummy problem with error message instead of failing - problem_text = 'Problem %s has an error:%s' % (self.location.url(), msg) - self.lcp = LoncapaProblem(problem_text, self.location.html_id(), instance_state, seed=seed, system=self.system) + problem_text = ('' + 'Problem %s has an error:%s' % + (self.location.url(), msg)) + self.lcp = LoncapaProblem( + problem_text, self.location.html_id(), + instance_state, seed=seed, system=self.system) else: raise @property def rerandomize(self): """ - Property accessor that returns self.metadata['rerandomize'] in a canonical form + Property accessor that returns self.metadata['rerandomize'] in a + canonical form """ rerandomize = self.metadata.get('rerandomize', 'always') if rerandomize in ("", "always", "true"): @@ -203,7 +212,10 @@ class CapaModule(XModule): except Exception, err: if self.system.DEBUG: log.exception(err) - msg = '[courseware.capa.capa_module] Failed to generate HTML for problem %s' % (self.location.url()) + msg = ( + '[courseware.capa.capa_module] ' + 'Failed to generate HTML for problem %s' % + (self.location.url())) msg += '

Error:

%s

' % str(err).replace('<', '<') msg += '

%s

' % traceback.format_exc().replace('<', '<') html = msg @@ -215,8 +227,8 @@ class CapaModule(XModule): 'weight': self.weight, } - # We using strings as truthy values, because the terminology of the check button - # is context-specific. + # We using strings as truthy values, because the terminology of the + # check button is context-specific. check_button = "Grade" if self.max_attempts else "Check" reset_button = True save_button = True @@ -242,7 +254,8 @@ class CapaModule(XModule): if not self.lcp.done: reset_button = False - # We don't need a "save" button if infinite number of attempts and non-randomized + # We don't need a "save" button if infinite number of attempts and + # non-randomized if self.max_attempts is None and self.rerandomize != "always": save_button = False diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index a04324237c..05f440c0f8 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -17,11 +17,12 @@ class CourseDescriptor(SequenceDescriptor): try: self.start = time.strptime(self.metadata["start"], "%Y-%m-%dT%H:%M") except KeyError: - self.start = time.gmtime(0) # The epoch - log.critical("Course loaded without a start date. " + str(self.id)) - except ValueError, e: - self.start = time.gmtime(0) # The epoch - log.critical("Course loaded with a bad start date. " + str(self.id) + " '" + str(e) + "'") + self.start = time.gmtime(0) #The epoch + log.critical("Course loaded without a start date. %s", self.id) + except ValueError as e: + self.start = time.gmtime(0) #The epoch + log.critical("Course loaded with a bad start date. %s '%s'", + self.id, e) def has_started(self): return time.gmtime() > self.start diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index 9a90afb896..da620e4889 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -19,7 +19,9 @@ class MakoModuleDescriptor(XModuleDescriptor): def __init__(self, system, definition=None, **kwargs): if getattr(system, 'render_template', None) is None: - raise TypeError('{system} must have a render_template function in order to use a MakoDescriptor'.format(system=system)) + raise TypeError('{system} must have a render_template function' + ' in order to use a MakoDescriptor'.format( + system=system)) super(MakoModuleDescriptor, self).__init__(system, definition, **kwargs) def get_context(self): @@ -29,4 +31,5 @@ class MakoModuleDescriptor(XModuleDescriptor): return {'module': self} def get_html(self): - return self.system.render_template(self.mako_template, self.get_context()) + return self.system.render_template( + self.mako_template, self.get_context()) diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 5527d4108e..ac03d92854 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -45,13 +45,17 @@ class Location(_LocationBase): """ return re.sub('_+', '_', INVALID_CHARS.sub('_', value)) - def __new__(_cls, loc_or_tag=None, org=None, course=None, category=None, name=None, revision=None): + def __new__(_cls, loc_or_tag=None, org=None, course=None, category=None, + name=None, revision=None): """ Create a new location that is a clone of the specifed one. location - Can be any of the following types: - string: should be of the form {tag}://{org}/{course}/{category}/{name}[/{revision}] + string: should be of the form + {tag}://{org}/{course}/{category}/{name}[/{revision}] + list: should be of the form [tag, org, course, category, name, revision] + dict: should be of the form { 'tag': tag, 'org': org, @@ -62,16 +66,19 @@ class Location(_LocationBase): } Location: another Location object - In both the dict and list forms, the revision is optional, and can be ommitted. + In both the dict and list forms, the revision is optional, and can be + ommitted. - Components must be composed of alphanumeric characters, or the characters '_', '-', and '.' + Components must be composed of alphanumeric characters, or the + characters '_', '-', and '.' - Components may be set to None, which may be interpreted by some contexts to mean - wildcard selection + Components may be set to None, which may be interpreted by some contexts + to mean wildcard selection """ - if org is None and course is None and category is None and name is None and revision is None: + if (org is None and course is None and category is None and + name is None and revision is None): location = loc_or_tag else: location = (loc_or_tag, org, course, category, name, revision) @@ -131,9 +138,11 @@ class Location(_LocationBase): def html_id(self): """ - Return a string with a version of the location that is safe for use in html id attributes + Return a string with a version of the location that is safe for use in + html id attributes """ - return "-".join(str(v) for v in self.list() if v is not None).replace('.', '_') + return "-".join(str(v) for v in self.list() + if v is not None).replace('.', '_') def dict(self): """ @@ -154,7 +163,8 @@ class Location(_LocationBase): class ModuleStore(object): """ - An abstract interface for a database backend that stores XModuleDescriptor instances + An abstract interface for a database backend that stores XModuleDescriptor + instances """ def get_item(self, location, depth=0): """ @@ -164,13 +174,16 @@ class ModuleStore(object): If any segment of the location is None except revision, raises xmodule.modulestore.exceptions.InsufficientSpecificationError - If no object is found at that location, raises xmodule.modulestore.exceptions.ItemNotFoundError + + If no object is found at that location, raises + xmodule.modulestore.exceptions.ItemNotFoundError location: Something that can be passed to Location - depth (int): An argument that some module stores may use to prefetch descendents of the queried modules - for more efficient results later in the request. The depth is counted in the number of - calls to get_children() to cache. None indicates to cache all descendents + depth (int): An argument that some module stores may use to prefetch + descendents of the queried modules for more efficient results later + in the request. The depth is counted in the number of calls to + get_children() to cache. None indicates to cache all descendents """ raise NotImplementedError @@ -182,9 +195,10 @@ class ModuleStore(object): location: Something that can be passed to Location - depth: An argument that some module stores may use to prefetch descendents of the queried modules - for more efficient results later in the request. The depth is counted in the number of calls - to get_children() to cache. None indicates to cache all descendents + depth: An argument that some module stores may use to prefetch + descendents of the queried modules for more efficient results later + in the request. The depth is counted in the number of calls to + get_children() to cache. None indicates to cache all descendents """ raise NotImplementedError @@ -228,4 +242,3 @@ class ModuleStore(object): in this modulestore. ''' raise NotImplementedError - diff --git a/common/lib/xmodule/xmodule/raw_module.py b/common/lib/xmodule/xmodule/raw_module.py index 2794e27dd6..2a4c04e512 100644 --- a/common/lib/xmodule/xmodule/raw_module.py +++ b/common/lib/xmodule/xmodule/raw_module.py @@ -8,7 +8,7 @@ log = logging.getLogger(__name__) class RawDescriptor(MakoModuleDescriptor, XmlDescriptor): """ - Module that provides a raw editing view of it's data and children + Module that provides a raw editing view of its data and children """ mako_template = "widgets/raw-edit.html" diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py index b39292c2ca..d435be627b 100644 --- a/common/lib/xmodule/xmodule/seq_module.py +++ b/common/lib/xmodule/xmodule/seq_module.py @@ -20,12 +20,15 @@ class_priority = ['video', 'problem'] class SequenceModule(XModule): ''' Layout module which lays out content in a temporal sequence ''' - js = {'coffee': [resource_string(__name__, 'js/src/sequence/display.coffee')]} + js = {'coffee': [resource_string(__name__, + 'js/src/sequence/display.coffee')]} css = {'scss': [resource_string(__name__, 'css/sequence/display.scss')]} js_module_name = "Sequence" - def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): - XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) + def __init__(self, system, location, definition, instance_state=None, + shared_state=None, **kwargs): + XModule.__init__(self, system, location, definition, + instance_state, shared_state, **kwargs) self.position = 1 if instance_state is not None: @@ -92,7 +95,8 @@ class SequenceModule(XModule): self.rendered = True def get_icon_class(self): - child_classes = set(child.get_icon_class() for child in self.get_children()) + child_classes = set(child.get_icon_class() + for child in self.get_children()) new_class = 'other' for c in class_priority: if c in child_classes: @@ -114,5 +118,6 @@ class SequenceDescriptor(MakoModuleDescriptor, XmlDescriptor): def definition_to_xml(self, resource_fs): xml_object = etree.Element('sequential') for child in self.get_children(): - xml_object.append(etree.fromstring(child.export_to_xml(resource_fs))) + xml_object.append( + etree.fromstring(child.export_to_xml(resource_fs))) return xml_object diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 996d31a83d..0c6d99fcf4 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -31,23 +31,28 @@ class Plugin(object): def load_class(cls, identifier, default=None): """ Loads a single class instance specified by identifier. If identifier - specifies more than a single class, then logs a warning and returns the first - class identified. + specifies more than a single class, then logs a warning and returns the + first class identified. - If default is not None, will return default if no entry_point matching identifier - is found. Otherwise, will raise a ModuleMissingError + If default is not None, will return default if no entry_point matching + identifier is found. Otherwise, will raise a ModuleMissingError """ if cls._plugin_cache is None: cls._plugin_cache = {} if identifier not in cls._plugin_cache: identifier = identifier.lower() - classes = list(pkg_resources.iter_entry_points(cls.entry_point, name=identifier)) + classes = list(pkg_resources.iter_entry_points( + cls.entry_point, name=identifier)) + if len(classes) > 1: - log.warning("Found multiple classes for {entry_point} with identifier {id}: {classes}. Returning the first one.".format( + log.warning("Found multiple classes for {entry_point} with " + "identifier {id}: {classes}. " + "Returning the first one.".format( entry_point=cls.entry_point, id=identifier, - classes=", ".join(class_.module_name for class_ in classes))) + classes=", ".join( + class_.module_name for class_ in classes))) if len(classes) == 0: if default is not None: @@ -79,9 +84,12 @@ class HTMLSnippet(object): def get_javascript(cls): """ Return a dictionary containing some of the following keys: + coffee: A list of coffeescript fragments that should be compiled and placed on the page - js: A list of javascript fragments that should be included on the page + + js: A list of javascript fragments that should be included on the + page All of these will be loaded onto the page in the CMS """ @@ -91,12 +99,15 @@ class HTMLSnippet(object): def get_css(cls): """ Return a dictionary containing some of the following keys: - css: A list of css fragments that should be applied to the html contents - of the snippet - sass: A list of sass fragments that should be applied to the html contents - of the snippet - scss: A list of scss fragments that should be applied to the html contents - of the snippet + + css: A list of css fragments that should be applied to the html + contents of the snippet + + sass: A list of sass fragments that should be applied to the html + contents of the snippet + + scss: A list of scss fragments that should be applied to the html + contents of the snippet """ return cls.css @@ -104,47 +115,70 @@ class HTMLSnippet(object): """ Return the html used to display this snippet """ - raise NotImplementedError("get_html() must be provided by specific modules - not present in {0}" + raise NotImplementedError( + "get_html() must be provided by specific modules - not present in {0}" .format(self.__class__)) class XModule(HTMLSnippet): ''' Implements a generic learning module. - Subclasses must at a minimum provide a definition for get_html in order to be displayed to users. + Subclasses must at a minimum provide a definition for get_html in order + to be displayed to users. See the HTML module for a simple example. ''' - # The default implementation of get_icon_class returns the icon_class attribute of the class - # This attribute can be overridden by subclasses, and the function can also be overridden - # if the icon class depends on the data in the module + # The default implementation of get_icon_class returns the icon_class + # attribute of the class + # + # This attribute can be overridden by subclasses, and + # the function can also be overridden if the icon class depends on the data + # in the module icon_class = 'other' - def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): + def __init__(self, system, location, definition, + instance_state=None, shared_state=None, **kwargs): ''' Construct a new xmodule system: A ModuleSystem allowing access to external resources + location: Something Location-like that identifies this xmodule - definition: A dictionary containing 'data' and 'children'. Both are optional - 'data': is JSON-like (string, dictionary, list, bool, or None, optionally nested). - This defines all of the data necessary for a problem to display that is intrinsic to the problem. - It should not include any data that would vary between two courses using the same problem + + definition: A dictionary containing 'data' and 'children'. Both are + optional + + 'data': is JSON-like (string, dictionary, list, bool, or None, + optionally nested). + + This defines all of the data necessary for a problem to display + that is intrinsic to the problem. It should not include any + data that would vary between two courses using the same problem (due dates, grading policy, randomization, etc.) - 'children': is a list of Location-like values for child modules that this module depends on - instance_state: A string of serialized json that contains the state of this module for - current student accessing the system, or None if no state has been saved - shared_state: A string of serialized json that contains the state that is shared between - this module and any modules of the same type with the same shared_state_key. This - state is only shared per-student, not across different students - kwargs: Optional arguments. Subclasses should always accept kwargs and pass them - to the parent class constructor. + + 'children': is a list of Location-like values for child modules that + this module depends on + + instance_state: A string of serialized json that contains the state of + this module for current student accessing the system, or None if + no state has been saved + + shared_state: A string of serialized json that contains the state that + is shared between this module and any modules of the same type with + the same shared_state_key. This state is only shared per-student, + not across different students + + kwargs: Optional arguments. Subclasses should always accept kwargs and + pass them to the parent class constructor. + Current known uses of kwargs: - metadata: SCAFFOLDING - This dictionary will be split into several different types of metadata - in the future (course policy, modification history, etc). - A dictionary containing data that specifies information that is particular - to a problem in the context of a course + + metadata: SCAFFOLDING - This dictionary will be split into + several different types of metadata in the future (course + policy, modification history, etc). A dictionary containing + data that specifies information that is particular to a + problem in the context of a course ''' self.system = system self.location = Location(location) @@ -217,16 +251,21 @@ class XModule(HTMLSnippet): def max_score(self): ''' Maximum score. Two notes: - * This is generic; in abstract, a problem could be 3/5 points on one randomization, and 5/7 on another - * In practice, this is a Very Bad Idea, and (a) will break some code in place (although that code - should get fixed), and (b) break some analytics we plan to put in place. + + * This is generic; in abstract, a problem could be 3/5 points on one + randomization, and 5/7 on another + + * In practice, this is a Very Bad Idea, and (a) will break some code + in place (although that code should get fixed), and (b) break some + analytics we plan to put in place. ''' return None def get_progress(self): - ''' Return a progress.Progress object that represents how far the student has gone - in this module. Must be implemented to get correct progress tracking behavior in - nesting modules like sequence and vertical. + ''' Return a progress.Progress object that represents how far the + student has gone in this module. Must be implemented to get correct + progress tracking behavior in nesting modules like sequence and + vertical. If this module has no notion of progress, return None. ''' @@ -240,13 +279,14 @@ class XModule(HTMLSnippet): class XModuleDescriptor(Plugin, HTMLSnippet): """ - An XModuleDescriptor is a specification for an element of a course. This could - be a problem, an organizational element (a group of content), or a segment of video, - for example. + An XModuleDescriptor is a specification for an element of a course. This + could be a problem, an organizational element (a group of content), or a + segment of video, for example. - XModuleDescriptors are independent and agnostic to the current student state on a - problem. They handle the editing interface used by instructors to create a problem, - and can generate XModules (which do know about student state). + XModuleDescriptors are independent and agnostic to the current student state + on a problem. They handle the editing interface used by instructors to + create a problem, and can generate XModules (which do know about student + state). """ entry_point = "xmodule.v1" module_class = XModule @@ -255,46 +295,58 @@ class XModuleDescriptor(Plugin, HTMLSnippet): inheritable_metadata = ( 'graded', 'start', 'due', 'graceperiod', 'showanswer', 'rerandomize', - # This is used by the XMLModuleStore to provide for locations for static files, - # and will need to be removed when that code is removed + # TODO: This is used by the XMLModuleStore to provide for locations for + # static files, and will need to be removed when that code is removed 'data_dir' ) - # A list of descriptor attributes that must be equal for the descriptors to be - # equal - equality_attributes = ('definition', 'metadata', 'location', 'shared_state_key', '_inherited_metadata') + # A list of descriptor attributes that must be equal for the descriptors to + # be equal + equality_attributes = ('definition', 'metadata', 'location', + 'shared_state_key', '_inherited_metadata') - # ============================= STRUCTURAL MANIPULATION =========================== + # ============================= STRUCTURAL MANIPULATION =================== def __init__(self, system, definition=None, **kwargs): """ Construct a new XModuleDescriptor. The only required arguments are the - system, used for interaction with external resources, and the definition, - which specifies all the data needed to edit and display the problem (but none - of the associated metadata that handles recordkeeping around the problem). + system, used for interaction with external resources, and the + definition, which specifies all the data needed to edit and display the + problem (but none of the associated metadata that handles recordkeeping + around the problem). - This allows for maximal flexibility to add to the interface while preserving - backwards compatibility. + This allows for maximal flexibility to add to the interface while + preserving backwards compatibility. - system: An XModuleSystem for interacting with external resources - definition: A dict containing `data` and `children` representing the problem definition + system: A DescriptorSystem for interacting with external resources + + definition: A dict containing `data` and `children` representing the + problem definition Current arguments passed in kwargs: - location: A xmodule.modulestore.Location object indicating the name and ownership of this problem - shared_state_key: The key to use for sharing StudentModules with other - modules of this type + + location: A xmodule.modulestore.Location object indicating the name + and ownership of this problem + + shared_state_key: The key to use for sharing StudentModules with + other modules of this type + metadata: A dictionary containing the following optional keys: - goals: A list of strings of learning goals associated with this module - display_name: The name to use for displaying this module to the user + goals: A list of strings of learning goals associated with this + module + display_name: The name to use for displaying this module to the + user format: The format of this module ('Homework', 'Lab', etc) graded (bool): Whether this module is should be graded or not start (string): The date for which this module will be available due (string): The due date for this module - graceperiod (string): The amount of grace period to allow when enforcing the due date + graceperiod (string): The amount of grace period to allow when + enforcing the due date showanswer (string): When to show answers for this module - rerandomize (string): When to generate a newly randomized instance of the module data + rerandomize (string): When to generate a newly randomized + instance of the module data """ self.system = system self.metadata = kwargs.get('metadata', {}) @@ -321,7 +373,8 @@ class XModuleDescriptor(Plugin, HTMLSnippet): self.metadata[attr] = metadata[attr] def get_children(self): - """Returns a list of XModuleDescriptor instances for the children of this module""" + """Returns a list of XModuleDescriptor instances for the children of + this module""" if self._child_instances is None: self._child_instances = [] for child_loc in self.definition.get('children', []): @@ -333,8 +386,9 @@ class XModuleDescriptor(Plugin, HTMLSnippet): def xmodule_constructor(self, system): """ - Returns a constructor for an XModule. This constructor takes two arguments: - instance_state and shared_state, and returns a fully nstantiated XModule + Returns a constructor for an XModule. This constructor takes two + arguments: instance_state and shared_state, and returns a fully + instantiated XModule """ return partial( self.module_class, @@ -344,7 +398,7 @@ class XModuleDescriptor(Plugin, HTMLSnippet): metadata=self.metadata ) - # ================================= JSON PARSING =================================== + # ================================= JSON PARSING =========================== @staticmethod def load_from_json(json_data, system, default_class=None): """ @@ -366,13 +420,14 @@ class XModuleDescriptor(Plugin, HTMLSnippet): Creates an instance of this descriptor from the supplied json_data. This may be overridden by subclasses - json_data: A json object specifying the definition and any optional keyword arguments for - the XModuleDescriptor - system: An XModuleSystem for interacting with external resources + json_data: A json object specifying the definition and any optional + keyword arguments for the XModuleDescriptor + + system: A DescriptorSystem for interacting with external resources """ return cls(system=system, **json_data) - # ================================= XML PARSING ==================================== + # ================================= XML PARSING ============================ @staticmethod def load_from_xml(xml_data, system, @@ -487,24 +542,33 @@ class ModuleSystem(object): ''' def __init__(self, ajax_url, track_function, get_module, render_template, replace_urls, - user=None, filestore=None, debug=False, xqueue_callback_url=None): + user=None, filestore=None, debug=False, + xqueue_callback_url=None): ''' Create a closure around the system environment. ajax_url - the url where ajax calls to the encapsulating module go. + track_function - function of (event_type, event), intended for logging or otherwise tracking the event. TODO: Not used, and has inconsistent args in different files. Update or remove. + get_module - function that takes (location) and returns a corresponding - module instance object. - render_template - a function that takes (template_file, context), and returns - rendered html. - user - The user to base the random number generator seed off of for this request - filestore - A filestore ojbect. Defaults to an instance of OSFS based at - settings.DATA_DIR. + module instance object. + + render_template - a function that takes (template_file, context), and + returns rendered html. + + user - The user to base the random number generator seed off of for this + request + + filestore - A filestore ojbect. Defaults to an instance of OSFS based + at settings.DATA_DIR. + replace_urls - TEMPORARY - A function like static_replace.replace_urls - that capa_module can use to fix up the static urls in ajax results. + that capa_module can use to fix up the static urls in + ajax results. ''' self.ajax_url = ajax_url self.xqueue_callback_url = xqueue_callback_url @@ -529,4 +593,3 @@ class ModuleSystem(object): def __str__(self): return str(self.__dict__) - diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py index a7fc686e45..49e0f79976 100644 --- a/common/lib/xmodule/xmodule/xml_module.py +++ b/common/lib/xmodule/xmodule/xml_module.py @@ -13,13 +13,19 @@ log = logging.getLogger(__name__) # TODO (cpennington): This was implemented in an attempt to improve performance, # but the actual improvement wasn't measured (and it was implemented late at night). # We should check if it hurts, and whether there's a better way of doing lazy loading + class LazyLoadingDict(MutableMapping): """ - A dictionary object that lazily loads it's contents from a provided - function on reads (of members that haven't already been set) + A dictionary object that lazily loads its contents from a provided + function on reads (of members that haven't already been set). """ def __init__(self, loader): + ''' + On the first read from this dictionary, it will call loader() to + populate its contents. loader() must return something dict-like. Any + elements set before the first read will be preserved. + ''' self._contents = {} self._loaded = False self._loader = loader @@ -70,10 +76,12 @@ _AttrMapBase = namedtuple('_AttrMap', 'metadata_key to_metadata from_metadata') class AttrMap(_AttrMapBase): """ - A class that specifies a metadata_key, a function to transform an xml attribute to be placed in that key, - and to transform that key value + A class that specifies a metadata_key, a function to transform an xml + attribute to be placed in that key, and to transform that key value """ - def __new__(_cls, metadata_key, to_metadata=lambda x: x, from_metadata=lambda x: x): + def __new__(_cls, metadata_key, + to_metadata=lambda x: x, + from_metadata=lambda x: x): return _AttrMapBase.__new__(_cls, metadata_key, to_metadata, from_metadata) @@ -93,7 +101,9 @@ class XmlDescriptor(XModuleDescriptor): # A dictionary mapping xml attribute names to functions of the value # that return the metadata key and value xml_attribute_map = { - 'graded': AttrMap('graded', lambda val: val == 'true', lambda val: str(val).lower()), + 'graded': AttrMap('graded', + lambda val: val == 'true', + lambda val: str(val).lower()), 'name': AttrMap('display_name'), } @@ -105,12 +115,14 @@ class XmlDescriptor(XModuleDescriptor): xml_object: An etree Element """ - raise NotImplementedError("%s does not implement definition_from_xml" % cls.__name__) + raise NotImplementedError( + "%s does not implement definition_from_xml" % cls.__name__) @classmethod def clean_metadata_from_xml(cls, xml_object): """ - Remove any attribute named in self.metadata_attributes from the supplied xml_object + Remove any attribute named in cls.metadata_attributes from the supplied + xml_object """ for attr in cls.metadata_attributes: if xml_object.get(attr) is not None: @@ -134,7 +146,7 @@ class XmlDescriptor(XModuleDescriptor): xml_data: A string of xml that will be translated into data and children for this module - system: An XModuleSystem for interacting with external resources + system: A DescriptorSystem for interacting with external resources org and course are optional strings that will be used in the generated modules url identifiers """ @@ -157,6 +169,7 @@ class XmlDescriptor(XModuleDescriptor): else: filepath = cls._format_filepath(xml_object.tag, filename) + # VS[compat] # TODO (cpennington): If the file doesn't exist at the right path, # give the class a chance to fix it up. The file will be written out again # in the correct format. @@ -243,4 +256,5 @@ class XmlDescriptor(XModuleDescriptor): """ Return a new etree Element object created from this modules definition. """ - raise NotImplementedError("%s does not implement definition_to_xml" % self.__class__.__name__) + raise NotImplementedError( + "%s does not implement definition_to_xml" % self.__class__.__name__)