[z3-checkins] r52153 - in z3/deliverance/trunk/deliverance: . test-data/urienv
tseaver at codespeak.net
tseaver at codespeak.net
Tue Mar 4 17:54:11 CET 2008
Author: tseaver
Date: Tue Mar 4 17:54:10 2008
New Revision: 52153
Added:
z3/deliverance/trunk/deliverance/test-data/urienv/
- copied from r52150, z3/deliverance/branches/urienv/deliverance/test-data/urienv/
Modified:
z3/deliverance/trunk/deliverance/test_wsgi.py
z3/deliverance/trunk/deliverance/utils.py
z3/deliverance/trunk/deliverance/wsgimiddleware.py
Log:
Merge urienv branch:
- (wsgimiddleware): Wrap long lines per PEP 8.
- (wsgimiddleware) Expose all middleware constructor arguments via
Paste config.
- (wsgimiddleware) Check for environmental overrides of theme URI, rule
URI, serializer, and apply.
- (utils) Added APIs for parsing non-string config values
('resolve_callable', 'resolve_dotted_or_egg', 'bool_from_string').
- (utils) Added APIs for getting / setting environmental overrides of
theme URI, rule URI, serializer.
Modified: z3/deliverance/trunk/deliverance/test_wsgi.py
==============================================================================
--- z3/deliverance/trunk/deliverance/test_wsgi.py (original)
+++ z3/deliverance/trunk/deliverance/test_wsgi.py Tue Mar 4 17:54:10 2008
@@ -23,6 +23,7 @@
aggregate_data = os.path.join(os.path.dirname(__file__), 'test-data', 'aggregate')
aggregate2_data = os.path.join(os.path.dirname(__file__), 'test-data', 'aggregate2')
ignore_data = os.path.join(os.path.dirname(__file__), 'test-data', 'ignore')
+urienv_data = os.path.join(os.path.dirname(__file__), 'test-data', 'urienv')
static_app = StaticURLParser(static_data)
tasktracker_app = StaticURLParser(tasktracker_data)
@@ -34,6 +35,7 @@
aggregate_app = StaticURLParser(aggregate_data)
aggregate2_app = StaticURLParser(aggregate2_data)
ignore_app = StaticURLParser(ignore_data)
+urienv_app = StaticURLParser(urienv_data)
def html_string_compare(astr, bstr):
"""
@@ -313,11 +315,51 @@
headers={'If-Modified-Since': formatdate(then-15)})
status = res.status
assert(status == 200)
+
+def do_rule_uri_environ(renderer_type, name):
+ from deliverance.utils import set_rule_uri
+ wsgi_app = DeliveranceMiddleware(urienv_app, 'theme.html', 'rules.xml',
+ renderer_type)
+ environ = {}
+ set_rule_uri(environ, 'rules2.xml')
+ app = TestApp(wsgi_app, extra_environ=environ)
+ res = app.get('/example.html')
+ res2 = app.get('/example_expected_rule_uri_environ.html?notheme')
+ html_string_compare(res.body, res2.body)
+
+def do_theme_uri_environ(renderer_type, name):
+ from deliverance.utils import set_theme_uri
+ wsgi_app = DeliveranceMiddleware(urienv_app, 'theme.html', 'rules.xml',
+ renderer_type)
+ environ = {}
+ set_theme_uri(environ, 'theme2.html')
+ app = TestApp(wsgi_app, extra_environ=environ)
+ res = app.get('/example.html')
+ res2 = app.get('/example_expected_theme_uri_environ.html?notheme')
+ html_string_compare(res.body, res2.body)
+def do_serializer_environ(renderer_type, name):
+ from deliverance.utils import set_serializer
+ wsgi_app = DeliveranceMiddleware(urienv_app, 'theme.html', 'rules.xml',
+ renderer_type)
+ environ = {}
+ set_serializer(environ, 'deliverance.test_wsgi:_uppercaseTextNodes')
+ app = TestApp(wsgi_app, extra_environ=environ)
+ res = app.get('/example.html')
+ res2 = app.get('/example_expected_uppercase.html?notheme')
+ html_string_compare(res.body, res2.body)
+
+def _uppercaseTextNodes(content):
+ from htmlserialize import tostring
+ return tostring(content,
+ doctype_pair=("-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/html4/loose.dtd")
+ ).upper()
RENDERER_TYPES = ['py', 'xslt']
-TEST_FUNCS = [ do_url, do_basic, do_text, do_tasktracker, do_xinclude, do_with_spaces, do_nycsr, do_necoro, do_guidesearch, do_ajax, do_aggregate, do_aggregate2, do_cache, do_ignore, do_ignore_header ]
+TEST_FUNCS = [ do_url, do_basic, do_text, do_tasktracker, do_xinclude, do_with_spaces, do_nycsr, do_necoro, do_guidesearch, do_ajax, do_aggregate, do_aggregate2, do_cache, do_ignore, do_ignore_header, do_rule_uri_environ, do_theme_uri_environ, do_serializer_environ ]
+
def test_all():
for renderer_type in RENDERER_TYPES:
for test_func in TEST_FUNCS:
Modified: z3/deliverance/trunk/deliverance/utils.py
==============================================================================
--- z3/deliverance/trunk/deliverance/utils.py (original)
+++ z3/deliverance/trunk/deliverance/utils.py Tue Mar 4 17:54:10 2008
@@ -535,3 +535,44 @@
text = etree.tostring(rule)
text = text.replace(' xmlns="http://www.plone.org/deliverance"', '')
return text
+
+# API for sharing overridden theme / rule URIs with other middleware layers.
+
+_THEME_URI_KEY = 'deliverance.theme_uri'
+
+def set_theme_uri(environ, uri):
+ environ[_THEME_URI_KEY] = uri
+
+def get_theme_uri(environ, default=None):
+ return environ.get(_THEME_URI_KEY, default)
+
+_RULE_URI_KEY = 'deliverance.rule_uri'
+
+def set_rule_uri(environ, uri):
+ environ[_RULE_URI_KEY] = uri
+
+def get_rule_uri(environ, default=None):
+ return environ.get(_RULE_URI_KEY, default)
+
+_SERIALIZER_KEY = 'deliverance.serializer'
+
+def set_serializer(environ, dotted_or_egg):
+ environ[_SERIALIZER_KEY] = dotted_or_egg
+
+def get_serializer(environ, default=None):
+ return resolve_callable(environ.get(_SERIALIZER_KEY, default))
+
+def resolve_callable(dotted_or_egg):
+ if isinstance(dotted_or_egg, basestring):
+ return resolve_dotted_or_egg(dotted_or_egg)
+ return dotted_or_egg
+
+def resolve_dotted_or_egg(dotted_or_egg):
+ from pkg_resources import EntryPoint
+ return EntryPoint.parse('x=%s' % dotted_or_egg).load(False)
+
+def bool_from_string(value):
+ if isinstance(value, basestring):
+ if value.lower() in ('false', 'no'):
+ return False
+ return bool(value)
Modified: z3/deliverance/trunk/deliverance/wsgimiddleware.py
==============================================================================
--- z3/deliverance/trunk/deliverance/wsgimiddleware.py (original)
+++ z3/deliverance/trunk/deliverance/wsgimiddleware.py Tue Mar 4 17:54:10 2008
@@ -13,9 +13,16 @@
from paste.request import construct_url
from paste.response import header_value, replace_header
from htmlserialize import tostring
+from deliverance.utils import bool_from_string
from deliverance.utils import DeliveranceError
from deliverance.utils import DELIVERANCE_ERROR_PAGE
-from deliverance.resource_fetcher import InternalResourceFetcher, FileResourceFetcher, ExternalResourceFetcher
+from deliverance.utils import get_theme_uri
+from deliverance.utils import get_rule_uri
+from deliverance.utils import get_serializer
+from deliverance.utils import resolve_callable
+from deliverance.resource_fetcher import InternalResourceFetcher
+from deliverance.resource_fetcher import FileResourceFetcher
+from deliverance.resource_fetcher import ExternalResourceFetcher
from deliverance import cache_utils
import sys
import datetime
@@ -27,12 +34,19 @@
DELIVERANCE_BASE_URL = 'deliverance.base-url'
DELIVERANCE_CACHE = 'deliverance.cache'
-IGNORE_EXTENSIONS = ['js','css','gif','jpg','jpeg','pdf','ps','doc','png','ico','mov','mpg','mpeg', 'mp3','m4a',
- 'txt','rtf', 'swf', 'wav', 'zip', 'wmv', 'ppt', 'gz', 'tgz', 'jar', 'xls', 'bmp', 'tif', 'tga',
- 'hqx', 'avi']
+IGNORE_EXTENSIONS = ['js', 'css', 'gif', 'jpg', 'jpeg', 'pdf', 'ps', 'doc',
+ 'png', 'ico', 'mov', 'mpg', 'mpeg', 'mp3', 'm4a', 'txt',
+ 'rtf', 'swf', 'wav', 'zip', 'wmv', 'ppt', 'gz', 'tgz',
+ 'jar', 'xls', 'bmp', 'tif', 'tga', 'hqx', 'avi',
+ ]
IGNORE_URL_PATTERN = re.compile("^.*\.(%s)$" % '|'.join(IGNORE_EXTENSIONS))
+def _toHTML(content):
+ return tostring(content,
+ doctype_pair=("-//W3C//DTD HTML 4.01 Transitional//EN",
+ "http://www.w3.org/TR/html4/loose.dtd"))
+
class DeliveranceMiddleware(object):
"""
a DeliveranceMiddleware object exposes a single deliverance
@@ -41,7 +55,7 @@
def __init__(self, app, theme_uri, rule_uri,
renderer='py', merge_cache_control=False,
- is_internal_uri=None):
+ is_internal_uri=None, serializer=None):
"""
initializer
@@ -60,11 +74,14 @@
should be considered 'internal'(passed to the
subapplication) and false if the requestshould be send
over the network.
+ serializer: dotted name or entry point indicdating a callable used
+ to post-process rendered output. Defaults to the '_toHTML' function
+ above.
"""
self.app = app
self.theme_uri = theme_uri
self.rule_uri = rule_uri
- self.merge_cache_control = merge_cache_control
+ self.merge_cache_control = bool_from_string(merge_cache_control)
if renderer == 'py':
import interpreter
@@ -73,11 +90,15 @@
import xslt
self._rendererType = xslt.Renderer
elif renderer is None or isinstance(renderer, basestring):
- raise ValueError("Unknown Renderer: %s - Expecting 'py' or 'xslt'" % renderer)
+ raise ValueError("Unknown Renderer: %s - Expecting 'py' or 'xslt'"
+ % renderer)
else:
self._rendererType = renderer
- self._is_internal_uri = is_internal_uri
+ self._is_internal_uri = resolve_callable(is_internal_uri)
+ if serializer is None:
+ serializer = _toHTML
+ self.serializer = serializer
def get_renderer(self, environ):
return self.create_renderer(environ)
@@ -88,11 +109,11 @@
information passed to the initializer. A new copy
of the theme and rules is retrieved.
"""
- theme = self.theme(environ)
- rule = self.rule(environ)
+ theme, theme_uri = self.theme(environ)
+ rule, rule_uri = self.rule(environ)
full_theme_uri = urlparse.urljoin(
construct_url(environ, with_path_info=False),
- self.theme_uri)
+ theme_uri)
def reference_resolver(href, parse, encoding=None):
text = self.get_resource(environ, href)
@@ -109,7 +130,7 @@
try:
parsedTheme = parseHTML(theme)
except Exception, message:
- newmessage = "Unable to parse theme page (" + self.theme_uri + ")"
+ newmessage = "Unable to parse theme page (" + theme_uri + ")"
if message:
newmessage += ":" + str(message)
raise DeliveranceError(newmessage)
@@ -124,33 +145,40 @@
theme=parsedTheme,
theme_uri=full_theme_uri,
rule=parsedRule,
- rule_uri=self.rule_uri,
+ rule_uri=rule_uri,
reference_resolver=reference_resolver)
- def rule(self, environ):
- """
+ def rule(self, environ=None):
+ """ environ -> (rule, rule_uri)
retrieves the data referred to by the rule_uri passed to the
initializer.
"""
+ if environ is None:
+ environ = {}
+ rule_uri = get_rule_uri(environ, self.rule_uri)
try:
- return self.get_resource(environ, self.rule_uri)
+ return (self.get_resource(environ, rule_uri), rule_uri)
except Exception, message:
- newmessage = "Unable to retrieve rules from " + self.rule_uri
+ newmessage = "Unable to retrieve rules from " + rule_uri
if message:
newmessage += ": " + str(message)
raise DeliveranceError(newmessage)
- def theme(self, environ):
- """
+ def theme(self, environ=None):
+ """ environ -> (theme, theme_uri)
+
retrieves the data referred to by the theme_uri passed to the
initializer.
"""
+ if environ is None:
+ environ = {}
+ theme_uri = get_theme_uri(environ, self.theme_uri)
try:
- return self.get_resource(environ, self.theme_uri)
+ return (self.get_resource(environ, theme_uri), theme_uri)
except Exception, message:
- message.public_html = 'Unable to retrieve theme page from %s: %s' % (
- self.theme_uri, message)
+ message.public_html = ('Unable to retrieve theme page from %s: %s'
+ % (theme_uri, message))
raise
def __call__(self, environ, start_response):
@@ -162,13 +190,16 @@
initializer.
"""
qs = environ.get('QUERY_STRING', '')
- environ[DELIVERANCE_BASE_URL] = construct_url(environ, with_path_info=False, with_query_string=False)
+ environ[DELIVERANCE_BASE_URL] = construct_url(environ,
+ with_path_info=False,
+ with_query_string=False)
environ[DELIVERANCE_CACHE] = {}
notheme = 'notheme' in qs
if environ.get('HTTP_X_REQUESTED_WITH', '') == 'XMLHttpRequest':
notheme = True
if notheme:
- # eliminate the deliverance notheme query argument for the subrequest
+ # eliminate the deliverance notheme query argument for
+ # the subrequest
if qs == 'notheme':
environ['QUERY_STRING'] = ''
elif qs.endswith('¬heme'):
@@ -227,17 +258,18 @@
type = header_value(headers, 'content-type')
if type is None:
return True # yerg, 304s can have no content-type
- return type.startswith('text/html') or type.startswith('application/xhtml+xml')
+ return (type.startswith('text/html') or
+ type.startswith('application/xhtml+xml'))
def filter_body(self, environ, body):
"""
- returns the result of the deliverance transformation on the string 'body'
- in the context of environ. The result is a string containing HTML.
+ returns the result of the deliverance transform on the string 'body'
+ in the context of environ. The result is a string containing HTML,
+ or whatever the configured serializer makes it.
"""
content = self.get_renderer(environ).render(parseHTML(body))
-
- return tostring(content, doctype_pair=("-//W3C//DTD HTML 4.01 Transitional//EN",
- "http://www.w3.org/TR/html4/loose.dtd"))
+ serializer = get_serializer(environ, self.serializer)
+ return serializer(content)
def rebuild_check(self, environ, start_response):
@@ -247,7 +279,8 @@
etag_map = {}
if 'HTTP_IF_NONE_MATCH' in environ:
- etag_map = cache_utils.parse_merged_etag(environ['HTTP_IF_NONE_MATCH'])
+ etag_map = cache_utils.parse_merged_etag(
+ environ['HTTP_IF_NONE_MATCH'])
tag = etag_map.get(content_url, None)
environ['HTTP_IF_NONE_MATCH'] = tag
if tag:
@@ -284,8 +317,8 @@
return (status, headers, body)
# got 304 Not Modified for content, check other resources
- rules = etree.XML(self.rule(environ))
- resources = self.get_resource_uris(rules)
+ rules = etree.XML(self.rule(environ)[0])
+ resources = self.get_resource_uris(rules, environ)
if self.any_modified(environ, resources, etag_map):
# something changed,
# get the content explicitly and give it back
@@ -369,7 +402,8 @@
else:
loc = ''
raise DeliveranceError(
- "Request for internal resource at %s (%r) failed with status code %r%s"
+ "Request for internal resource at %s (%r) failed "
+ "with status code %r%s"
% (construct_url(environ), path_info, status,
loc))
@@ -427,14 +461,18 @@
return cleaned
- def get_resource_uris(self, rules):
+ def get_resource_uris(self, rules, environ=None):
"""
retrieves a list of uris pointing to the resources that
are components of rendering (excluding content)
"""
+ if environ is None:
+ environ = {}
resources = Set()
- resources.add(self.rule_uri)
- resources.add(self.theme_uri)
+ rule_uri = get_rule_uri(environ, self.rule_uri)
+ resources.add(rule_uri)
+ theme_uri = get_theme_uri(environ, self.theme_uri)
+ resources.add(theme_uri)
for rule in rules:
href = rule.get("href",None)
@@ -444,16 +482,16 @@
return list(resources)
- def check_modification(self, environ, uri, httpdate_since=None, etag=None):
+ def check_modification(self, environ, uri, httpdate_since=None, etag=None):
"""
- if httpdate_since is set to an httpdate the If-Modified-Since HTTP header
- is used to check for modification
+ if httpdate_since is set to an httpdate the If-Modified-Since HTTP
+ header is used to check for modification
- if etag is set to an etag for the resource, the If-None-Match HTTP header
- is used to check for modification
+ if etag is set to an etag for the resource, the If-None-Match HTTP
+ header is used to check for modification
- the resulting (status, headers, body) tuple for the request is stored in
- environ[DELIVERANCE_CACHE][uri].
+ the resulting (status, headers, body) tuple for the request is stored
+ in environ[DELIVERANCE_CACHE][uri].
"""
@@ -498,16 +536,39 @@
# blacklisting can happen here as well
return re.match(IGNORE_URL_PATTERN, url) is not None
+
+def always_external(uri, environ):
+ """Always return False so the external loader is used.
+
+ o Configure in paste config using the following:
+
+ is_internal_uri = deliverance.wsgimiddleware:always_external
+ """
+ return False
+
+
def make_filter(app, global_conf,
- theme_uri=None, rule_uri=None,
- renderer='py'):
+ theme_uri=None,
+ rule_uri=None,
+ renderer='py',
+ merge_cache_control=False,
+ is_internal_uri=None,
+ serializer=None,
+ ):
+ """ Configure DeliveranceError via Paste config.
+ """
assert theme_uri is not None, (
"You must give a theme_uri")
assert rule_uri is not None, (
"You must give a rule_uri")
- return DeliveranceMiddleware(
- app, theme_uri, rule_uri,
- renderer=renderer)
+ return DeliveranceMiddleware(app=app,
+ theme_uri=theme_uri,
+ rule_uri=rule_uri,
+ renderer=renderer,
+ merge_cache_control=merge_cache_control,
+ is_internal_uri=is_internal_uri,
+ serializer=serializer,
+ )
_http_equiv_re = re.compile(r'<meta\s+[^>]*http-equiv="?content-type"?[^>]*>', re.I|re.S)
_head_re = re.compile(r'<head[^>]*>', re.I|re.S)
More information about the z3-checkins
mailing list