Changeset 1510

Show
Ignore:
Timestamp:
03/27/08 16:59:44 (7 months ago)
Author:
JensDiemer
Message:

add a experimental new cache middleware
-merge pagestats into cache middleware
-add unitest for the cache
-move setup_debug() from the views into the common middleware
-update other tests

Location:
trunk/pylucid
Files:
2 added
1 removed
13 modified

Legend:

Unmodified
Added
Removed
  • trunk/pylucid/PyLucid/db/__init__.py

    r1079 r1510  
    1 #!/usr/bin/python 
    2 # -*- coding: UTF-8 -*- 
    3  
    4 """ 
    5 shared DB Objects. 
    6 """ 
    7  
    8 import page 
  • trunk/pylucid/PyLucid/index.py

    r1507 r1510  
    1818""" 
    1919 
    20 import datetime, md5 
    21  
    2220from django.http import HttpResponse, HttpResponsePermanentRedirect, \ 
    2321                                                           HttpResponseRedirect 
     22from django.conf import settings 
    2423from django.template import RequestContext 
    25 from django.core.cache import cache 
     24from django.utils.safestring import mark_safe 
    2625from django.utils.translation import ugettext as _ 
    27 from django.utils.safestring import mark_safe 
    28 from django.conf import settings 
    29  
    30 from PyLucid import models 
    31  
     26 
     27from PyLucid.models import Page 
    3228from PyLucid.system import plugin_manager 
     29from PyLucid.system.URLs import URLs 
     30from PyLucid.system.page_msg import PageMessages 
    3331from PyLucid.system.response import SimpleStringIO 
    3432from PyLucid.system.exceptions import AccessDenied 
    35 from PyLucid.system.page_msg import PageMessages 
     33from PyLucid.system.context_processors import add_dynamic_context, add_css_tag 
    3634from PyLucid.system.detect_page import get_current_page_obj, \ 
    3735                                                            get_default_page_id 
    38 from PyLucid.system.URLs import URLs 
    39 from PyLucid.system.context_processors import add_dynamic_context, add_css_tag 
    40 from PyLucid.system.utils import setup_debug 
     36from PyLucid.tools.utils import escape, escape_django_tags 
    4137from PyLucid.tools.content_processors import apply_markup, \ 
    4238                                    render_string_template, redirect_warnings 
    43 from PyLucid.tools.utils import escape, escape_django_tags 
    4439from PyLucid.plugins_internal.page_style.page_style import replace_add_data 
    4540 
     
    135130 
    136131 
    137 def patch_response_headers(response, cache_timeout, ETag, last_modified): 
    138     """ 
    139     Adds some useful headers to the given HttpResponse object: 
    140         ETag, Last-Modified, Expires and Cache-Control 
    141  
    142     Original version: django.utils.cache.patch_response_headers() 
    143     """ 
    144     response['ETag'] = ETag 
    145     response['Last-Modified'] = last_modified.strftime( 
    146         '%a, %d %b %Y %H:%M:%S GMT' 
    147     ) 
    148     now = datetime.datetime.utcnow() 
    149     expires = now + datetime.timedelta(0, cache_timeout) 
    150     response['Expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT') 
    151  
    152  
    153 def get_cached_data(url): 
    154     """ 
    155     -Build the cache_key from the given url. Use the last page shortcut. 
    156     -retuned the cache_key and the page data. 
    157     """ 
    158     if url == "": 
    159         # Request without a shortcut -> request the default page 
    160         shortcut = "/" 
    161     else: 
    162         # Note: We use append_slash, but the url pattern striped the last 
    163         #     slash out. 
    164         # e.g.: '/page1/page2/page3' -> ['/page1/page2', 'page3'] -> 'page3' 
    165         shortcut = url.rsplit("/", 1)[-1] 
    166  
    167     cache_key = settings.PAGE_CACHE_PREFIX + shortcut 
    168     #print "Used cache key:", cache_key 
    169  
    170     # Get the page data from the cache. 
    171     response = cache.get(cache_key) 
    172  
    173     return cache_key, response 
    174  
    175  
    176132def index(request, url): 
    177133    """ 
     
    181137    the page shortcut from the url. 
    182138    """ 
    183     # Cache only for anonymous users. Otherwise users how are log-in don't see 
    184     # the dynamic integrate admin menu. 
    185     use_cache = request.user.is_anonymous() 
    186  
    187     if use_cache: 
    188         # Try to get the cms page request from the cache 
    189         cache_key, response = get_cached_data(url) 
    190         if response: 
    191             # This page has been cached in the past, use the cache data: 
    192             return response 
    193  
    194     setup_debug(request) 
    195  
    196139    try: 
    197140        current_page_obj = get_current_page_obj(request, url) 
     
    211154 
    212155    context = _get_context(request, current_page_obj) 
     156 
    213157    # Get the response for the requested cms page: 
    214158    response = _render_cms_page(request, context) 
    215159 
    216     if use_cache: 
    217         # It's a anonymous user -> Cache the cms page. 
    218         cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS 
    219         # Add some headers for the browser cache 
    220         patch_response_headers( 
    221             response, cache_timeout, 
    222             ETag = md5.new(cache_key).hexdigest(), 
    223             last_modified = current_page_obj.lastupdatetime, 
    224         ) 
    225         # Save the page into the cache 
    226         cache.set(cache_key, response, cache_timeout) 
     160    if getattr(request, "_use_cache", None) == None: 
     161        # Set _use_cache information for the PyLucid cache middleware, but only 
     162        # if it was set to true or false in the past 
     163        request._use_cache = True 
    227164 
    228165    return response 
     166 
    229167 
    230168def _get_page(request, page_id): 
     
    233171    TODO: Check int(page_id)! 
    234172    """ 
    235     setup_debug(request) 
    236  
    237173    try: 
    238         current_page_obj = models.Page.objects.get(id=int(page_id)) 
    239     except models.Page.DoesNotExist: 
     174        current_page_obj = Page.objects.get(id=int(page_id)) 
     175    except Page.DoesNotExist: 
    240176        # The ID in the url is wrong -> goto the default page 
    241177        default_page_id = get_default_page_id() 
    242         current_page_obj = models.Page.objects.get(id=default_page_id) 
     178        current_page_obj = Page.objects.get(id=default_page_id) 
    243179 
    244180        user = request.user 
     
    256192 
    257193    return current_page_obj 
     194 
    258195 
    259196def handle_command(request, page_id, module_name, method_name, url_args): 
     
    329266    return HttpResponsePermanentRedirect(url) 
    330267 
     268 
    331269def permalink(request, page_id): 
    332270    """ 
  • trunk/pylucid/PyLucid/install/tests.py

    r1302 r1510  
    44""" 
    55 
     6import os, cgi, sys, time 
     7 
     8from django.conf import settings 
     9 
     10from django import newforms as forms 
     11from django.core import management 
     12from django.core.cache import cache 
     13 
    614from PyLucid.install.BaseInstall import BaseInstall 
    715from PyLucid.system.response import SimpleStringIO 
    8  
    9 from django.core import management 
    10 from django import newforms as forms 
    11  
    12 import os, cgi, sys, time 
    1316 
    1417 
     
    2225        print txt 
    2326        print "-"*80 
     27 
     28    def _test_cache(self): 
     29        """ 
     30        Test the cache backend. 
     31        """ 
     32        if settings.CACHE_BACKEND.startswith("dummy"): 
     33            return 
     34        self._print_infoline( 
     35            "Test cache backend '%s':" % settings.CACHE_BACKEND 
     36        ) 
     37        cache_key = "cache test" 
     38        content = "A cache test content..." 
     39        cache_timeout = 50 
     40        print "Save into cache with key '%s'." % cache_key 
     41        cache.set(cache_key, content, cache_timeout) 
     42        print "Try to get the saved content from the cache." 
     43        cached_content = cache.get(cache_key) 
     44        if cached_content == None: 
     45            print " * Get None back. Cache didn't work!" 
     46            return 
     47        elif cached_content==content: 
     48            print " * Cache works fine ;)" 
     49        else: 
     50            # Should never appears 
     51            print " * Error! Cache content not the same!" 
     52 
     53        print "Try to delete the cache entry." 
     54        cache.delete(cache_key) 
     55        cached_content = cache.get(cache_key) 
     56        if cached_content == None: 
     57            print "OK, entry deleted." 
     58        else: 
     59            print "Error: entry not deleted!" 
     60 
    2461 
    2562    def _verbose_try_import(self, module_name): 
     
    109146 
    110147    def print_info(self): 
     148        self._test_cache() 
     149 
    111150        self._print_infoline("Tests for the memcache backend:") 
    112151        self._verbose_try_import("cmemcache") 
  • trunk/pylucid/PyLucid/middlewares/common.py

    r1507 r1510  
    3131from PyLucid.system.exceptions import LowLevelError 
    3232from PyLucid.system.template import render_help_page 
     33from PyLucid.system.utils import setup_debug 
    3334 
    3435 
     
    5859    """ 
    5960    def process_request(self, request): 
     61        # add the attribute "debug" to the request object. 
     62        setup_debug(request) 
     63 
    6064        try: 
    6165            session_middleware.process_request(request) 
     
    6468        except Exception, e: 
    6569            raise_non_table_error(e) 
    66  
    6770 
    6871    def process_view(self, request, view_func, view_args, view_kwargs): 
  • trunk/pylucid/PyLucid/models.py

    r1507 r1510  
    1818import os, posixpath, pickle 
    1919 
     20from django.conf import settings 
    2021from django.db import models 
    21 from django.contrib.auth.models import User, Group 
    22 from django.contrib.auth.models import UNUSABLE_PASSWORD 
     22from django.core.cache import cache 
     23from django.contrib.auth.models import User, Group, UNUSABLE_PASSWORD 
    2324from django.utils.translation import ugettext as _ 
    24 from django.conf import settings 
    2525 
    2626from PyLucid.tools.shortcuts import getUniqueShortcut 
    2727from PyLucid.tools import crypt 
    2828from PyLucid.system.utils import get_uri_base 
     29#from PyLucid.db.cache import delete_page_cache 
    2930 
    3031 
     
    3839) 
    3940 
     41def delete_page_cache(): 
     42    """ 
     43    Delete all pages in the cache. 
     44    Needed, if: 
     45        - A template has been edited 
     46        - The menu changes (edit the page name, position, parent link) 
     47    TODO: move this function from models.py into a other nice place... 
     48    """ 
     49    for items in Page.objects.values('shortcut').iterator(): 
     50        shortcut = items["shortcut"] 
     51        cache_key = settings.PAGE_CACHE_PREFIX + shortcut 
     52        cache.delete(cache_key) 
     53 
    4054 
    4155class Page(models.Model): 
    4256    """ 
    4357    A CMS Page Object 
     58 
     59    TODO: We should refactor the "pre_save" behavior, use signals: 
     60    http://code.djangoproject.com/wiki/Signals 
    4461    """ 
    4562    # Explicite id field, so we can insert a help_text ;) 
     
    239256            self._check_parent(self.id) 
    240257 
     258        # Delete all pages in the cache. 
     259        # FIXME: This is only needed, if the menu changed: e.g.: if the page 
     260        # position, shortcut, parent cahnges... 
     261        delete_page_cache() 
     262 
    241263        # Rebuild shortcut / make shortcut unique: 
    242264        self._prepare_shortcut() 
     265 
     266        # Delete old page cache, if exist 
     267        cache_key = settings.PAGE_CACHE_PREFIX + self.shortcut 
     268        cache.delete(cache_key) 
    243269 
    244270        super(Page, self).save() # Call the "real" save() method 
     
    711737            pass 
    712738 
     739        #Delete the page cache if a stylesheet was edited. 
     740        delete_page_cache() 
     741 
    713742        super(Style, self).save() # Call the "real" save() method 
    714743 
     
    729758    description = models.TextField() 
    730759    content = models.TextField() 
     760 
     761    def save(self): 
     762        """ 
     763        Delete the page cache if a template was edited. 
     764        """ 
     765        delete_page_cache() 
     766 
     767        super(Template, self).save() # Call the "real" save() method 
    731768 
    732769    class Admin: 
  • trunk/pylucid/PyLucid/settings_example.py

    r1505 r1510  
    9090    'PyLucid.middlewares.common.PyLucidCommonMiddleware', 
    9191 
     92    'PyLucid.middlewares.cache.CacheMiddleware', 
     93 
    9294    'django.middleware.common.CommonMiddleware', 
    9395    'django.middleware.doc.XViewMiddleware', 
     
    9597    # Add a human readable anchor to every html headline: 
    9698    'PyLucid.middlewares.headline_anchor.HeadlineAnchor', 
    97  
    98     # Insert a statistic line into the generated page: 
    99     'PyLucid.middlewares.pagestats.PageStatsMiddleware', 
    10099) 
    101100 
  • trunk/pylucid/PyLucid/template_addons/filters.py

    r1420 r1510  
    5858    """ 
    5959    Converts a time duration into a friendly text representation. 
    60     Note: Used in PageStatsMiddleware, too. 
     60    Note: Used in the PyLucid cache middleware, too. 
    6161    """ 
    6262    if t<1: 
  • trunk/pylucid/tests/internal_pages.py

    r1476 r1510  
    5454        Page.objects.all().delete() # Delete all existins pages 
    5555 
    56         self.template = tests.create_template(TEST_TEMPLATE) 
     56        self.template = tests.create_template(content = TEST_TEMPLATE) 
    5757 
    5858        # Create the test pages defined in content_test_utils.py 
  • trunk/pylucid/tests/plugin_backlinks.py

    r1454 r1510  
    4141        Page.objects.all().delete() # Delete all existins pages 
    4242 
    43         self.template = tests.create_template("{% lucidTag back_links %}") 
     43        self.template = tests.create_template( 
     44            content = "{% lucidTag back_links %}" 
     45        ) 
    4446 
    4547        # Create the test pages defined in content_test_utils.py 
     
    7476        print_last_page="True" 
    7577        """ 
    76  
    7778        self.template.content = ( 
    7879            '{% lucidTag back_links print_last_page="True" %}' 
  • trunk/pylucid/tests/plugin_main_menu.py

    r1454 r1510  
    3636        Page.objects.all().delete() # Delete all existins pages 
    3737 
    38         self.template = tests.create_template("{% lucidTag main_menu %}") 
     38        self.template = tests.create_template( 
     39            content = "{% lucidTag main_menu %}" 
     40        ) 
    3941 
    4042        tests.create_pages(tests.TEST_PAGES, template=self.template) 
     
    9799        Page.objects.all().delete() # Delete all existins pages 
    98100 
    99         self.template = tests.create_template("{% lucidTag main_menu %}") 
     101        self.template = tests.create_template( 
     102            content = "{% lucidTag main_menu %}" 
     103        ) 
    100104 
    101105    def test_base(self): 
  • trunk/pylucid/tests/stylesheet.py

    r1476 r1510  
    5252        Page.objects.all().delete() # Delete all existins pages 
    5353 
    54         self.template = tests.create_template("{% lucidTag page_style %}") 
     54        self.template = tests.create_template( 
     55            content = "{% lucidTag page_style %}" 
     56        ) 
    5557        self.style = tests.create_stylesheet( 
    5658            name = "TestStyle", content = TEST_STYLE_CONTENT, 
     
    9092        """ Return the stylesheet link contains in the root cms page. """ 
    9193        response = self.client.get("/") 
     94        from_cache = response.get("from_cache", None) 
     95        print from_cache 
    9296        content = response.content 
    9397        links = self._exctract_stylelinks(content) 
     
    150154        must_link = self._get_stylelink() 
    151155        is_link = self._get_content_link() 
    152         assert is_link == must_link 
     156        self.failUnlessEqual(must_link, is_link) 
    153157 
    154158    def test_command_link(self): 
  • trunk/pylucid/tests/utils/BrowserDebug.py

    r1501 r1510  
    5858    stack_info = "".join(stack) 
    5959 
    60     info = ( 
    61         "\n<br /><hr />\n" 
    62         "<h3>Unittest info</h3>\n" 
    63         "<dl>\n" 
    64         "<dt>url:</dt><dd>%s</dd>\n" 
    65         "<dt>traceback:</dt><dd><pre>%s</pre></dd>\n" 
    66         "</dl>\n" 
    67         "</body>" 
    68     ) % (url, stack_info) 
    69  
    70     content = content.replace("</body>", info) 
     60    if "</body>" in content: 
     61        info = ( 
     62            "\n<br /><hr />\n" 
     63            "<h3>Unittest info</h3>\n" 
     64            "<dl>\n" 
     65            "<dt>url:</dt><dd>%s</dd>\n" 
     66            "<dt>response info:</dt><dd><pre>%s</pre></dd>\n" 
     67            "<dt>traceback:</dt><dd><pre>%s</pre></dd>\n" 
     68            "</dl>\n" 
     69            "</body>" 
     70        ) % (url, response, stack_info) 
     71        content = content.replace("</body>", info) 
     72    else: 
     73        # Not a html page? 
     74        content += "\n<pre>\n" 
     75        content += "-" * 79 
     76        content += ( 
     77            "\nUnittest info\n" 
     78            "=============\n" 
     79            "url: %s\n" 
     80            "response info:\n%s\n" 
     81            "traceback:\n%s\n</pre>" 
     82        ) % (url, response, stack_info) 
    7183 
    7284 
  • trunk/pylucid/tests/__init__.py

    r1507 r1510  
    225225            is_list2 = pprint.pformat(is_list) 
    226226            should_be_list2 = pprint.pformat(should_be_list) 
     227            print "\nThe two lists are not the same:" 
    227228            for line in make_diff(should_be_list2, is_list2): 
    228229                print line 
     
    313314 
    314315    # _________________________________________________________________________ 
    315     # common tests: 
     316    # link snapshot: 
     317 
    316318    def link_snapshot_test(self, snapshot): 
    317319        """ 
     
    330332 
    331333        self.assertLists(is_links, should_be_links, sort=False) 
     334 
     335    def create_link_snapshot(self, print_result=True): 
     336        """ 
     337        Build a a reference snapshot for a unittest. 
     338        Display it via pprint and returned it, too. 
     339        Usefull for copy&paste the output into this source file :) 
     340        """ 
     341        if print_result: 
     342            print "Build a snapshot for the unittest compare:" 
     343            print "-"*79 
     344        data = {} 
     345        for page in Page.objects.all(): 
     346            url = page.get_absolute_url() 
     347 
     348            response = self.client.get(url) 
     349            self.failUnlessEqual(response.status_code, 200) 
     350 
     351            content = response.content.strip() 
     352            links = self.get_links(content) 
     353 
     354            data[url] = links 
     355 
     356        if print_result: 
     357            pprint.pprint(data) 
     358            print "-"*79 
     359        return data 
    332360 
    333361 
     
    391419 
    392420 
    393 def create_template(content): 
     421def create_template(**kwargs): 
    394422    """ 
    395423    Delete all existing templates, create a new one and return the instance. 
     
    397425    Template.objects.all().delete() 
    398426 
    399     template = Template(content = content) 
     427    template = Template(**kwargs) 
    400428    template.save() 
    401429    return template 
     
    421449    default_template = Template.objects.all()[0] 
    422450    default_style = Style.objects.all()[0] 
    423     default_markup = 0 # html withdout TinyMCE 
     451    default_markup = 0 # html without TinyMCE 
    424452 
    425453    page = Page( 
    426454        name             = data.get("name", "New Page"), 
    427455        shortcut         = data.get("shortcut", None), 
     456        content          = data.get("content", "TestPageContent"), 
    428457        template         = data.get("template", default_template), 
    429458        style            = data.get("style", default_style),