Skip to content
This repository has been archived by the owner on Aug 4, 2021. It is now read-only.

Commit

Permalink
Use django-tools local sync cache for sync the plugin urls in a multi…
Browse files Browse the repository at this point in the history
…-threaded environment.
  • Loading branch information
jedie committed Jul 25, 2013
1 parent 67335f6 commit 0592abc
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 69 deletions.
9 changes: 3 additions & 6 deletions pylucid_project/apps/pylucid/signals.py
Expand Up @@ -31,9 +31,6 @@
@receiver(post_save, sender=PluginPage)
@receiver(post_delete, sender=PluginPage)
def update_plugin_urls(sender, **kwargs):
"""
FIXME: This signal would only recreate the urls from the current thread!
"""
log.debug("update_plugin_urls called with: %s" % repr(kwargs))
from pylucid_project.system.pylucid_plugins import pylucid_plugin_urls
pylucid_plugin_urls.renew_plugin_urls()
# import and connect signal here agains import loops
from pylucid_project.system.pylucid_plugins import clear_plugin_url_caches
clear_plugin_url_caches(sender, **kwargs)
114 changes: 52 additions & 62 deletions pylucid_project/system/pylucid_plugins.py
Expand Up @@ -17,15 +17,17 @@
from django.conf import settings
from django.conf.urls import patterns, include
from django.core import urlresolvers
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import RegexURLPattern
from django.http import HttpResponse, HttpResponseServerError
from django.core.urlresolvers import RegexURLResolver, \
_resolver_cache, _ns_resolver_cache, _callable_cache
from django.http import HttpResponse
from django.utils.importlib import import_module
from django.utils.log import getLogger
from django.views.decorators.csrf import csrf_protect

from django_tools.local_sync_cache.local_sync_cache import LocalSyncCache

from pylucid_project.utils.python_tools import has_init_file
from pylucid_project.utils.url_debug import log_urls
from pylucid_project.utils.url_debug import log_urls, debug_log_urls


# see: http://www.pylucid.org/permalink/443/how-to-display-debug-information
Expand All @@ -35,54 +37,32 @@
# PYLUCID_PLUGINS = None

_PLUGIN_OBJ_CACHE = {} # cache for PyLucidPlugin.get_plugin_object()
_PLUGIN_URL_CACHE = {} # cache for PyLucidPlugin.get_prefix_urlpatterns()
_PLUGIN_URL_CACHE = LocalSyncCache(id="plugin_url_cache") # cache for PyLucidPlugin.get_prefix_urlpatterns()



class PluginNotOnSite(Exception):
""" PluginPage doesn't exist on current page. """
pass


class PluginURLPattern(RegexURLPattern):
def _placeholder_view(self, *args, **kwargs):
raise HttpResponseServerError("plugin url placeholder view called?")

def __init__(self):
super(PluginURLPattern, self).__init__(
regex="^ / / placeholder / / $", # FIXME: should never match
callback=self._placeholder_view,
name="Plugin url placeholder",
)

def resolve(self, path):
return

# Use django-tools local sync cache for sync the plugin urls in
# a multi-threaded environment.
PLUGIN_URLS_SYNC_DICT = LocalSyncCache(id="plugin url patterns")


class PluginURLs(object):
_URL_INSERTED = False
class PluginURLPattern(RegexURLResolver):
"""
Handle all url patterns from PyLucid plugins.
All urls are in the current PageTree. It would be renew after a the
page tree would be changed by user. e.g.: Create/move a PluginPage.
"""
def __init__(self):
self.plugin_url_index = None
self.root_urlconf = None

def init2(self):
self.root_urlconf = import_module(settings.ROOT_URLCONF)
root_urlpatterns = self.root_urlconf.urlpatterns

for no, p in enumerate(root_urlpatterns):
if isinstance(p, PluginURLPattern):
self.plugin_url_index = no
break

if self.plugin_url_index is None:
raise ImproperlyConfigured("No PluginURLPattern found in root of urls.py!")

log.debug("PluginURLPattern found at index: %i" % self.plugin_url_index)

def update_plugin_urls(self):
if self.plugin_url_index is None:
self.init2()
pass

def _get_plugin_patterns(self):
log.debug("PluginURLPattern._get_plugin_patterns")
from pylucid_project.apps.pylucid.models.pluginpage import PluginPage

plugin_pages = PluginPage.objects.all()
Expand All @@ -92,34 +72,46 @@ def update_plugin_urls(self):
plugin_instance = plugin_page.get_plugin()

prefixed_urlpatterns = plugin_instance.get_prefix_urlpatterns(url_prefix, plugin_page.urls_filename)
# log_urls(urlpatterns=prefixed_urlpatterns)
log_urls(urlpatterns=prefixed_urlpatterns)
pattern_list += prefixed_urlpatterns

debug_log_urls(pattern_list)
log_urls(urlpatterns=pattern_list)
plugin_patterns = urlresolvers.RegexURLResolver(r'', pattern_list)
plugin_patterns = RegexURLResolver(r'', pattern_list)
return plugin_patterns

log.debug("insert %i plugin urls at index %i" % (len(pattern_list), self.plugin_url_index))
self.root_urlconf.urlpatterns[self.plugin_url_index] = plugin_patterns
# log_urls()
def __getattr__(self, name):
try:
plugin_patterns = PLUGIN_URLS_SYNC_DICT["plugin_patterns"]
except KeyError:
# First time started or django-tools local sync cache was cleared
# in this thread or in a other thread.

def renew_plugin_urls(self):
"""
FIXME: This would only recreate the urls from this thread!
"""
log.debug("renew_plugin_urls()")
_PLUGIN_URL_CACHE.clear()
self.update_plugin_urls()
log.debug("Clear django.core.urlresolvers cache")
_resolver_cache.clear() # Maps URLconf modules to RegexURLResolver instances.
_ns_resolver_cache.clear() # Maps namespaces to RegexURLResolver instances.
_callable_cache.clear() # Maps view and url pattern names to their view functions.

# get the plugin url patterns fresh
plugin_patterns = self._get_plugin_patterns()
PLUGIN_URLS_SYNC_DICT["plugin_patterns"] = plugin_patterns

return getattr(plugin_patterns, name)

def insert_plugin_urls(self):
if self._URL_INSERTED:
return
self._URL_INSERTED = True
log.debug("insert_plugin_urls() called.")
self.update_plugin_urls()

def clear_plugin_url_caches(sender, **kwargs):
"""
signal handler for clear the plugin url caches.
connected in:
pylucid_project.apps.pylucid.signals.update_plugin_urls
"""
# clear the django-tools local sync cache, that will be clear all
# caches for every thread in a multi-threaded environment
PLUGIN_URLS_SYNC_DICT.clear()

pylucid_plugin_urls = PluginURLs()

#-------------------------------------------------------------------------------


class PyLucidPlugin(object):
Expand All @@ -146,8 +138,6 @@ def __init__(self, pkg_path, section, pkg_dir, plugin_name):
else:
self.template_dir = None

pylucid_plugin_urls.insert_plugin_urls()

def __unicode__(self):
return u"PyLucid plugin %r (%r)" % (self.name, self.installed_apps_string)

Expand Down
3 changes: 2 additions & 1 deletion pylucid_project/urls.py
Expand Up @@ -16,6 +16,7 @@
from django.contrib import admin
from django.http import HttpResponse
from django.views.defaults import server_error, page_not_found

from pylucid_project.system.pylucid_plugins import PluginURLPattern


Expand Down Expand Up @@ -91,7 +92,7 @@
)

urlpatterns += patterns('',
# Placehoder for urls from plugins. Would be dynamicly changed with information from database
# All PyLucid plugin urls. Would be dynamicly changed with information from database
PluginURLPattern(),

url('^', include('pylucid.urls')),
Expand Down
34 changes: 34 additions & 0 deletions pylucid_project/utils/url_debug.py
Expand Up @@ -123,6 +123,40 @@ def _extract_views_from_urlpatterns(self, urlpatterns, base=''):
return views



class DebugResolve(object):
def __init__(self, instance):
self.instance = instance
self.origin_resolve = instance.resolve

def __call__(self, path):
result = self.origin_resolve(path)
log.debug(
"resolve %s with %r -> %s" % (
repr(path), self.instance.regex.pattern, repr(result)
)
)
return result


def debug_log_urls(urlpatterns, base=''):
"""
add log output to every url resolve call.
"""
for p in urlpatterns:
if isinstance(p, RegexURLPattern):
log.debug("add debug to RegexURLPattern: %s" % repr(p))
p.resolve = DebugResolve(p)
elif isinstance(p, RegexURLResolver):
try:
patterns = p.url_patterns
except ImportError:
continue
debug_log_urls(patterns, base + p.regex.pattern)
else:
log.debug("Do nothing with", p)


def log_urls(urlpatterns=None, hide=None, only=None):
"""
e.g.:
Expand Down

0 comments on commit 0592abc

Please sign in to comment.