Centralize startup code, and execute in all contexts

Inspired by: http://eldarion.com/blog/2013/02/14/entry-point-hook-django-projects/
Moves startup code to lms.startup and cms.startup, and calls the startup
methods in wsgi.py and manage.py for both projects.
This commit is contained in:
Calen Pennington
2013-08-14 16:50:34 -04:00
parent 0e4b114278
commit 407b02b358
32 changed files with 170 additions and 174 deletions

View File

@@ -0,0 +1,14 @@
from importlib import import_module
from django.conf import settings
def autostartup():
"""
Execute app.startup:run() for all installed django apps
"""
for app in settings.INSTALLED_APPS:
try:
mod = import_module('{}.startup')
if hasattr(mod, 'run'):
mod.run()
except ImportError:
continue

View File

@@ -386,13 +386,6 @@ class ModuleStore(object):
"""
raise NotImplementedError
def set_modulestore_configuration(self, config_dict):
'''
Allows for runtime configuration of the modulestore. In particular this is how the
application (LMS/CMS) can pass down Django related configuration information, e.g. caches, etc.
'''
raise NotImplementedError
def get_modulestore_type(self, course_id):
"""
Returns a type which identifies which modulestore is servicing the given
@@ -405,13 +398,14 @@ class ModuleStoreBase(ModuleStore):
'''
Implement interface functionality that can be shared.
'''
def __init__(self):
def __init__(self, metadata_inheritance_cache_subsystem=None, request_cache=None, modulestore_update_signal=None):
'''
Set up the error-tracking logic.
'''
self._location_errors = {} # location -> ErrorLog
self.modulestore_configuration = {}
self.modulestore_update_signal = None # can be set by runtime to route notifications of datastore changes
self.metadata_inheritance_cache_subsystem = metadata_inheritance_cache_subsystem
self.modulestore_update_signal = modulestore_update_signal
self.request_cache = request_cache
def _get_errorlog(self, location):
"""
@@ -455,27 +449,6 @@ class ModuleStoreBase(ModuleStore):
return c
return None
@property
def metadata_inheritance_cache_subsystem(self):
"""
Exposes an accessor to the runtime configuration for the metadata inheritance cache
"""
return self.modulestore_configuration.get('metadata_inheritance_cache_subsystem', None)
@property
def request_cache(self):
"""
Exposes an accessor to the runtime configuration for the request cache
"""
return self.modulestore_configuration.get('request_cache', None)
def set_modulestore_configuration(self, config_dict):
"""
This is the base implementation of the interface, all we need to do is store
two possible configurations as attributes on the class
"""
self.modulestore_configuration = config_dict
def namedtuple_to_son(namedtuple, prefix=''):
"""

View File

@@ -8,8 +8,17 @@ from __future__ import absolute_import
from importlib import import_module
from django.conf import settings
from django.core.cache import get_cache, InvalidCacheBackendError
from django.dispatch import Signal
from xmodule.modulestore.loc_mapper_store import LocMapperStore
# We may not always have the request_cache module available
try:
from request_cache.middleware import RequestCache
HAS_REQUEST_CACHE = True
except ImportError:
HAS_REQUEST_CACHE = False
_MODULESTORES = {}
FUNCTION_KEYS = ['render_template']
@@ -39,7 +48,20 @@ def create_modulestore_instance(engine, options):
if key in _options and isinstance(_options[key], basestring):
_options[key] = load_function(_options[key])
if HAS_REQUEST_CACHE:
request_cache = RequestCache.get_request_cache()
else:
request_cache = None
try:
metadata_inheritance_cache = get_cache('mongo_metadata_inheritance')
except InvalidCacheBackendError:
metadata_inheritance_cache = get_cache('default')
return class_(
metadata_inheritance_cache_subsystem=metadata_inheritance_cache,
request_cache=request_cache,
modulestore_update_signal=Signal(providing_args=['modulestore', 'course_id', 'location']),
**_options
)

View File

@@ -17,12 +17,12 @@ class MixedModuleStore(ModuleStoreBase):
"""
ModuleStore that can be backed by either XML or Mongo
"""
def __init__(self, mappings, stores):
def __init__(self, mappings, stores, **kwargs):
"""
Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a
collection of other modulestore configuration informations
"""
super(MixedModuleStore, self).__init__()
super(MixedModuleStore, self).__init__(**kwargs)
self.modulestores = {}
self.mappings = mappings
@@ -132,14 +132,6 @@ class MixedModuleStore(ModuleStoreBase):
"""
return self._get_modulestore_for_courseid(course_id).get_parent_locations(location, course_id)
def set_modulestore_configuration(self, config_dict):
"""
This implementation of the interface method will pass along the configuration to all ModuleStore
instances
"""
for store in self.modulestores.values():
store.set_modulestore_configuration(config_dict)
def get_modulestore_type(self, course_id):
"""
Returns a type which identifies which modulestore is servicing the given

View File

@@ -270,15 +270,18 @@ class MongoModuleStore(ModuleStoreBase):
def __init__(self, host, db, collection, fs_root, render_template,
port=27017, default_class=None,
error_tracker=null_error_tracker,
user=None, password=None, **kwargs):
user=None, password=None, mongo_options=None, **kwargs):
super(MongoModuleStore, self).__init__()
super(MongoModuleStore, self).__init__(**kwargs)
if mongo_options is None:
mongo_options = {}
self.collection = pymongo.connection.Connection(
host=host,
port=port,
tz_aware=True,
**kwargs
**mongo_options
)[db][collection]
if user is not None and password is not None:

View File

@@ -50,15 +50,18 @@ class SplitMongoModuleStore(ModuleStoreBase):
port=27017, default_class=None,
error_tracker=null_error_tracker,
user=None, password=None,
mongo_options=None,
**kwargs):
ModuleStoreBase.__init__(self)
if mongo_options is None:
mongo_options = {}
self.db = pymongo.database.Database(pymongo.MongoClient(
host=host,
port=port,
tz_aware=True,
**kwargs
**mongo_options
), db)
self.course_index = self.db[collection + '.active_versions']

View File

@@ -5,9 +5,16 @@ from uuid import uuid4
from xmodule.tests import DATA_DIR
from xmodule.modulestore import Location, MONGO_MODULESTORE_TYPE, XML_MODULESTORE_TYPE
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.mixed import MixedModuleStore
from xmodule.modulestore.xml_importer import import_from_xml
# Mixed modulestore depends on django, so we'll manually configure some django settings
# before importing the module
from django.conf import settings
if not settings.configured:
settings.configure()
from xmodule.modulestore.mixed import MixedModuleStore
HOST = 'localhost'
PORT = 27017
@@ -234,22 +241,3 @@ class TestMixedModuleStore(object):
assert_equals(Location(parents[0]).org, 'edX')
assert_equals(Location(parents[0]).course, 'toy')
assert_equals(Location(parents[0]).name, '2012_Fall')
# pylint: disable=W0212
def test_set_modulestore_configuration(self):
config = {'foo': 'bar'}
self.store.set_modulestore_configuration(config)
assert_equals(
config,
self.store._get_modulestore_for_courseid(IMPORT_COURSEID).modulestore_configuration
)
assert_equals(
config,
self.store._get_modulestore_for_courseid(XML_COURSEID1).modulestore_configuration
)
assert_equals(
config,
self.store._get_modulestore_for_courseid(XML_COURSEID2).modulestore_configuration
)

View File

@@ -257,7 +257,7 @@ class XMLModuleStore(ModuleStoreBase):
"""
An XML backed ModuleStore
"""
def __init__(self, data_dir, default_class=None, course_dirs=None, load_error_modules=True):
def __init__(self, data_dir, default_class=None, course_dirs=None, load_error_modules=True, **kwargs):
"""
Initialize an XMLModuleStore from data_dir
@@ -269,7 +269,7 @@ class XMLModuleStore(ModuleStoreBase):
course_dirs: If specified, the list of course_dirs to load. Otherwise,
load all course dirs
"""
super(XMLModuleStore, self).__init__()
super(XMLModuleStore, self).__init__(**kwargs)
self.data_dir = path(data_dir)
self.modules = defaultdict(dict) # course_id -> dict(location -> XModuleDescriptor)

View File

@@ -8,10 +8,9 @@ from pkg_resources import resource_string
from .capa_module import ComplexEncoder
from .x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from .timeinfo import TimeInfo
from xblock.core import Dict, String, Scope, Boolean, Integer, Float
from xblock.core import Dict, String, Scope, Boolean, Float
from xmodule.fields import Date, Timedelta
from xmodule.open_ended_grading_classes.peer_grading_service import PeerGradingService, GradingServiceError, MockPeerGradingService
@@ -104,7 +103,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
if self.use_for_single_location:
try:
self.linked_problem = modulestore().get_instance(self.system.course_id, self.link_to_location)
self.linked_problem = self.system.get_module(self.link_to_location)
except ItemNotFoundError:
log.error("Linked location {0} for peer grading module {1} does not exist".format(
self.link_to_location, self.location))
@@ -632,3 +631,11 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
non_editable_fields = super(PeerGradingDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([PeerGradingFields.due, PeerGradingFields.graceperiod])
return non_editable_fields
def get_required_module_descriptors(self):
"""Returns a list of XModuleDescritpor instances upon which this module depends, but are
not children of this module"""
if self.use_for_single_location:
return [self.system.load_item(self.link_to_location)]
else:
return []

View File

@@ -2,7 +2,6 @@ from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from lxml import etree
from mako.template import Template
from xmodule.modulestore.django import modulestore
class CustomTagModule(XModule):
@@ -56,7 +55,7 @@ class CustomTagDescriptor(RawDescriptor):
# cdodge: look up the template as a module
template_loc = self.location.replace(category='custom_tag_template', name=template_name)
template_module = modulestore().get_instance(system.course_id, template_loc)
template_module = system.load_item(template_loc)
template_module_data = template_module.data
template = Template(template_module_data)
return template.render(**params)

View File

@@ -322,7 +322,8 @@ class CapaModuleTest(unittest.TestCase):
# We have to set up Django settings in order to use QueryDict
from django.conf import settings
settings.configure()
if not settings.configured:
settings.configure()
# Valid GET param dict
valid_get_dict = self._querydict_from_dict({'input_1': 'test',

View File

@@ -24,9 +24,6 @@ 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.modulestore import Location
from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.content import StaticContent
from xblock.core import Scope, String, Boolean, Float, List, Integer
import datetime