""" Django WSGI Paste-enabled wrapper """ __id__ = "$Id$" import sys import os import random import coerce_settings import django.conf from paste.util.threadinglocal import local from paste.wsgilib import catch_errors from paste.evalexception import EvalException from django.conf import global_settings as django_global_settings from django.core.handlers.wsgi import WSGIHandler from django.core.servers.basehttp import AdminMediaHandler # We use virtual module for fake settings # for support multiple Django-projects/apps with # different settings in one Paste-config # in a threaded environment. class FakeSettings(object): """ Fake replacement for django.conf.settings """ def __init__(self): # make fake local data be thread-local self._FAKE_local = local() def _FAKE_install(self): # replace django.conf.settings by self sys.modules['django.conf.settings'] = self django.conf.settings = self # and check that all correct from django.conf import settings as S assert S is self, "Seems that django.conf.settings is not replaced by FakeSettings" def _FAKE_attach(self, settings): """ Attach current settings to global one """ # be shure, that setings will be attached only one time assert not hasattr(self._FAKE_local, 'settings'), \ "Settings can be attached only ones" self._FAKE_local.settings = settings assert settings.DATABASE_ENGINE, \ "settings.DATABASE_ENGINE must exist and must not to be empty" # some sort of check from django.conf import settings as django_settings assert django_settings.DATABASE_ENGINE == settings.DATABASE_ENGINE, \ "Seems that django.conf.settings is not replaced by FakeSettings" def _FAKE_detach(self): """ Detach settings """ assert hasattr(self._FAKE_local, 'settings'), \ "I can't detach settings which are not attached" del self._FAKE_local.settings def __getattr__(self, attr): if not hasattr(self._FAKE_local, 'settings'): if attr == '__file__': # This one I don't really mind raise AttributeError("Is it really happens? " + \ "You want to retrieve __file__ but I'm a virtual module") raise AssertionError( "No settings have been attached to this request") try: return getattr(self._FAKE_local.settings, attr) except (KeyError, AttributeError), e: raise AttributeError( "The attribute %s is not found in %r" % (attr, self._FAKE_local.settings)) FAKE_SETTINGS = FakeSettings() class ProjectApplication(object): def __init__(self, settings): """ An Django-project WSGI-app with touching/detouching local conf """ self.settings = settings def __call__(self, environ, start_response): """ Touch/detouch settings on each request """ def deregister(exc_info=None): """ Settings detacher """ FAKE_SETTINGS._FAKE_detach() FAKE_SETTINGS._FAKE_attach(self.settings) app = AdminMediaHandler(WSGIHandler()) # deregister settings on request end return catch_errors( app, environ, start_response, deregister, deregister) def make_project_app(global_conf, **local_conf): # I don't like this either, the project directory # should be a proper setuptools package sys.path.append(os.path.dirname(global_conf['here'])) conf = global_conf.copy() conf.update(local_conf) settings = coerce_settings.coerce(django_global_settings, conf) if settings.get('project_base'): project_directory = settings['project_base'] project_name = os.path.basename(project_directory) sys.path.append(os.path.dirname(project_directory)) # sure that settings virtual module have semi-unique name # for disabling import cache in Python vmod_name = "djangopaste_settings_%04d" % random.randint(0,1024) settings_vmod = coerce_settings.make_module(settings, vmod_name) FAKE_SETTINGS._FAKE_install() app = ProjectApplication(settings_vmod) if settings.get('DEBUG'): app = EvalException(app) return app