''', 'deliverance.middleware.DeliveranceMiddleware._message_template')
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py Tue Jul 1 22:21:15 2008
@@ -7,7 +7,9 @@
from deliverance.util.converters import asbool, html_quote
from deliverance.selector import Selector
from lxml import etree
+from lxml.html import document_fromstring
from tempita import html
+import copy
CONTENT_ATTRIB = 'x-a-marker-attribute-for-deliverance'
@@ -137,6 +139,8 @@
assert isinstance(value, tuple), (
"Unexpected value: %r (for attribute %s)" % (
value, attr))
+ if value == ('warn', 'first'):
+ return None
handler, pos = value
if pos == 'last':
text = '%s:%s' % (handler, pos)
@@ -194,29 +198,8 @@
# Set to true in subclasses if the move attribute means something:
move_supported = True
- def describe_self(self):
- return self.log_description(log=None)
-
def __str__(self):
- return self.describe_self()
-
- def describe_content_elements(self, els, children=False):
- """
- A text description of a list of content elements, for use in
- log messages and errors.
- """
- text = ', '.join(el.tag for el in els)
- if children:
- return 'children of %s' % text
- else:
- return text
-
- def describe_theme_element(self, el):
- """
- A text description of a theme element, for use in log messages
- and errors.
- """
- return el.tag
+ return self.log_description(log=None)
@classmethod
def compile_selector(cls, el, attr, source_location):
@@ -259,6 +242,7 @@
originating in the content are not selectable).
"""
type, elements, attributes = selector(doc)
+ orig_elements = list(elements)
if theme:
bad_els = []
for el in elements:
@@ -277,15 +261,22 @@
if log is None or url is None:
return body
link = log.link_to(url, source=source, line=line, selector=selector)
- return '%s' % (html_quote(link), body)
+ return '%s' % (html_quote(link), body)
if log:
request_url = log.request.url
else:
request_url = None
- parts = ['<%s' % linked_item(self.source_location, self.name)]
+ parts = ['<%s' % linked_item(self.source_location, self.name, source=True)]
if getattr(self, 'content', None):
body = 'content="%s"' % html_quote(self.content)
- parts.append(linked_item(request_url, body, selector=self.content))
+ if self.content_href:
+ if request_url:
+ content_url = urlparse.urljoin(request_url, self.content_href)
+ else:
+ content_url = self.content_href
+ else:
+ content_url = request_url
+ parts.append(linked_item(content_url, body, selector=self.content))
if getattr(self, 'content_href', None):
dest = self.content_href
if request_url:
@@ -293,12 +284,12 @@
body = 'href="%s"' % html_quote(self.content_href)
parts.append(linked_item(dest, body, source=True))
if self.move_supported and not getattr(self, 'move', False):
- parts.append('move="1"')
+ parts.append('move="0"')
v = getattr(self, 'nocontent', 'warn')
if v != 'warn':
parts.append(self.format_error('nocontent', v))
v = getattr(self, 'manycontent', ('warn', None))
- if v != ('warn', None):
+ if v != ('warn', 'first'):
parts.append(self.format_error('manycontent', v))
if getattr(self, 'theme', None):
body = 'theme="%s"' % html_quote(self.theme)
@@ -308,12 +299,44 @@
if v != 'warn':
parts.append(self.format_error('notheme', v))
v = getattr(self, 'manytheme', ('warn', None))
- if v != ('warn', None):
+ if v != ('warn', 'first'):
parts.append(self.format_error('manytheme', v))
## FIXME: add source_location
return html(' '.join(parts) + ' />')
+ def format_tags(self, elements, include_name=True):
+ if not elements:
+ if include_name:
+ return 'no elements'
+ else:
+ return '(none)'
+ text = ', '.join('<%s>' % el.tag for el in elements)
+ if include_name:
+ if len(elements) > 1:
+ return 'elements %s' % text
+ else:
+ return 'element %s' % text
+ else:
+ return text
+ def format_tag(self, tag, include_name=False):
+ return self.format_tags([tag], include_name=include_name)
+
+ def format_attribute_names(self, attributes, include_name=True):
+ if not attributes:
+ if include_name:
+ return 'no attributes'
+ else:
+ return '(none)'
+ text = ', '.join(attributes)
+ if include_name:
+ if len(attributes) > 1:
+ return 'attributes %s' % text
+ else:
+ return 'attribute %s' % text
+ else:
+ return text
+
class TransformAction(AbstractAction):
# Abstract class for the rules that move from the content to the theme (replace, append, prepend)
@@ -361,13 +384,17 @@
"""
if self.content_href:
## FIXME: check response type
- content_doc = resource_fetcher(self.content_href).body
+ ## FIXME: Join content_href with request url?
+ content_resp = resource_fetcher(self.content_href)
+ log.debug(self, 'Fetching resource from href="%s": %s',
+ self.content_href, content_resp.status)
+ content_doc = document_fromstring(content_resp.body, base_url=self.content_href)
if not self.if_content_matches(content_doc, log):
return
content_type, content_els, content_attributes = self.select_elements(self.content, content_doc, theme=False)
if not content_els:
if self.nocontent == 'abort':
- log.debug(self, 'aborting theme because no content matches rule content="%s"', self.content)
+ log.debug(self, 'aborting theming because no content matches rule content="%s"', self.content)
raise AbortTheme('No content matches content="%s"' % self.content)
elif self.nocontent == 'ignore':
log_meth = log.debug
@@ -379,6 +406,7 @@
attributes = self.join_attributes(content_attributes, theme_attributes)
if not theme_els:
if self.notheme == 'abort':
+ log.debug(self, 'aborting theming because no theme elements match rule theme="%s"', self.theme)
raise AbortTheme('No theme element matches theme="%s"' % self.theme)
elif self.notheme == 'ignore':
log_meth = log.debug
@@ -387,19 +415,25 @@
log_meth(self, 'skipping rule because no theme element matches rule theme="%s"', self.theme)
return
if len(theme_els) > 1:
- if self.manytheme[0] == 'warn':
- log.warn(self, '%s elements match theme="%s", using the %s match',
- len(theme_els), self.theme, self.manytheme[1])
- pass
- elif self.manytheme[0] == 'abort':
+ if self.manytheme[0] == 'abort':
+ log.debug(self, 'aborting theming because %i elements (%s) match theme="%s"',
+ len(theme_els), self.format_tags(theme_els, include_name=False), self.theme)
raise AbortTheme('Many elements match theme="%s"' % self.theme)
+ elif self.manytheme[0] == 'warn':
+ log_meth = log.warn
+ else:
+ log_meth = log.debug
if self.manytheme[1] == 'first':
- theme_els = [theme_els[0]]
+ theme_el = theme_els[0]
else:
- theme_els = [theme_els[-1]]
- theme_el = theme_els[0]
+ theme_el = theme_els[-1]
+ log_meth(self, '%s elements match theme="%s", using the %s match',
+ len(theme_els), self.theme, self.manytheme[1])
+ else:
+ theme_el = theme_els[0]
if not self.move and theme_type in ('children', 'elements'):
- self.log.debug(self, 'content elements are being copied into theme (not moved)')
+ ## FIXME: is this message necessary?
+ log.debug(self, 'content elements are being copied into theme (not moved)')
content_els = copy.deepcopy(content_els)
mark_content_els(content_els)
self.apply_transformation(content_type, content_els, attributes, theme_type, theme_el, log)
@@ -439,6 +473,13 @@
def apply_transformation(self, content_type, content_els, attributes, theme_type, theme_el, log):
if theme_type == 'children':
existing_children = len(theme_el) or theme_el.text
+ theme_empty = not len(theme_el) and not theme_el.text
+ if len(theme_el):
+ log_text = 'and removed the chilren and text of the theme element'
+ elif theme_el.text:
+ log_text = 'and removed the text content of the theme element'
+ else:
+ log_text = '(the theme was already empty)'
theme_el[:] = []
theme_el.text = ''
if content_type == 'elements':
@@ -446,11 +487,15 @@
# If we aren't working with copies then we have to move the tails up as we remove the elements:
for el in reversed(content_els):
move_tail_upward(el)
+ verb = 'Moving'
else:
# If we are working with copies, then we can just throw away the tails:
for el in content_els:
el.tail = None
+ verb = 'Copying'
theme_el.extend(content_els)
+ log.debug(self, '%s %s from content into theme element %s %s',
+ verb, self.format_tags(content_els), self.format_tag(theme_el), log_text)
elif content_type == 'children':
text, els = self.prepare_content_children(content_els)
add_text(theme_el, text)
@@ -460,6 +505,12 @@
# elements.
for el in content_els:
el.getparent().remove(el)
+ log.debug(self, 'Moving children of content %s into theme element %s, '
+ 'and removing now-empty content elements %s',
+ self.format_tags(content_els), self.format_tag(theme_el), log_text)
+ else:
+ log.debug(self, 'Copying children of content %s into theme element %s %s',
+ self.format_tags(content_els), self.format_tag(theme_el), log_text)
else:
assert 0
@@ -471,10 +522,14 @@
if self.move:
for el in reversed(content_els):
move_tail_upwards(el)
+ verb = 'moved'
else:
for el in content_els:
el.tail = None
+ verb = 'copied'
parent[pos:pos+1] = content_els
+ log.debug(self, 'Replaced the theme element %s with the content %s (%s)',
+ self.format_tag(theme_el), self.format_tags(content_els), verb)
elif content_type == 'children':
text, els = self.prepare_content_children(content_els)
if pos == 0:
@@ -485,6 +540,12 @@
if self.move:
for el in content_els:
el.getparent().remove(el)
+ log.debug(self, 'Replaced the theme element %s with the children of the content %s, '
+ 'and removed the now-empty content element(s)',
+ self.format_tag(theme_el), self.format_tags(content_els))
+ else:
+ log.debug(self, 'Replaced the theme element %s with copies of the children of the content %s',
+ self.format_tag(theme_el), self.format_tags(content_els))
else:
assert 0
@@ -493,20 +554,25 @@
assert content_type == 'attributes'
if len(content_els) > 1:
if self.manycontent[0] == 'abort':
- log.debug(self, 'aborting because %s elements in the content match content="%s"',
- len(content_els), self.content)
+ log.debug(self, 'aborting because %i elements in the content (%s) match content="%s"',
+ len(content_els), self.format_tags(content_els, include_name=False), self.content)
raise AbortTheme()
else:
if self.manycontent[0] == 'warn':
log_meth = log.warn
else:
log_meth = log.debug
- log_meth(self, '%s elements match content="%s" (but only one expected), using the %s match',
- len(content_els, self.content, self.manycontent[1]))
+ log_meth(self, '%s elements match content="%s" (%s) when only one is expected, using the %s match',
+ len(content_els), self.content, self.format_tags(content_els, include_name=False), self.manycontent[1])
if self.manycontent[1] == 'first':
content_els = [content_els[0]]
else:
content_els = [content_els[-1]]
+ if theme_el.attrib:
+ log_text = ' and cleared all existing theme attributes'
+ else:
+ log_text = ''
+ ## FIXME: should this only clear the named attribute? (when attributes are named)
theme_el.attrib.clear()
if attributes:
c_attrib = content_els[0].attrib
@@ -517,17 +583,31 @@
for name in attributes:
if name in c_attrib:
del c_attrib[name]
+ log_text += ' and removed the attributes from the content'
+ ## FIXME: only list attributes that were actually found?
+ log.debug(self, 'Copied the %s from the content element %s to the '
+ 'theme element %s%s',
+ self.format_attribute_names(attributes), self.format_tag(content_els[0]),
+ self.format_tag(theme_el), log_text)
else:
theme_el.attrib.update(content_els[0].attrib)
if self.move:
content_els[0].attrib.clear()
+ log_text += ' and removed all attributes from the content'
+ log.debug(self, 'Moved all the attributes from the content element %s to the '
+ 'theme element %s%s',
+ self.format_tag(content_els[0]), self.format_tag(theme_el), log_text)
if theme_type == 'tag':
+ ## FIXME: warn about manycontent
assert content_type == 'tag'
+ old_tag = theme_el.tag
theme_el.tag = content_els[0].tag
theme_el.attrib.clear()
theme_el.attrib.update(content_els[0].attrib)
# "move" in this case doesn't mean anything
+ log.debug(self, 'Changed the tag name of the theme element <%s> to the name of the content element: %s',
+ old_tag, self.format_tag(content_els[0]))
_actions['replace'] = Replace
@@ -553,15 +633,21 @@
if self.move:
for el in reversed(content_els):
move_tail_upwards(el)
+ verb = 'Moving'
else:
for el in content_els:
el.tail = None
+ verb = 'Copying'
if self._append:
theme_el.extend(content_els)
+ pos_text = 'end'
else:
add_tail(content_els[-1], theme_el.text)
theme_el.text = None
theme_el[:0] = content_els
+ pos_text = 'beginning'
+ log.debug(self, '%s content %s to the %s of theme element %s',
+ verb, self.format_tags(content_els), pos_text, self.format_tag(theme_el))
elif content_type == 'children':
text, els = self.prepare_content_children(content_els)
if self._append:
@@ -570,10 +656,23 @@
else:
add_text(theme_el, text)
theme_el.extend(els)
+ pos_text = 'end'
else:
add_tail(els[-1], theme_el.text)
theme_el.text = text
theme_el[:0] = els
+ pos_text = 'beginning'
+ if self.move:
+ for el in reversed(content_els):
+ move_tail_upwards(el)
+ el.getparent().remove(el)
+ verb = 'Moving'
+ log_text = ' and removing the now-empty content element(s)'
+ else:
+ verb = 'Copying'
+ log_text = ''
+ log.debug(self, '%s the children of content %s to the %s of the theme element %s%s',
+ verb, self.format_tags(content_els), pos_text, self.format_tag(theme_el), log_text)
else:
assert 0
@@ -584,61 +683,120 @@
if self.move:
for el in reversed(content_els):
move_tail_upwards(el)
+ verb = 'Moving'
else:
for el in content_els:
el.tail = None
+ verb = 'Copying'
if self._append:
parent[pos+1:pos+1] = content_els
+ pos_text = 'after'
else:
parent[pos:pos] = content_els
+ pos_text = 'before'
+ log.debug(self, '%s content %s %s the theme element %s',
+ verb, self.format_tags(content_els), pos_text, self.format_tag(theme_el))
elif content_type == 'children':
text, els = self.prepare_content_children(content_els)
if self._append:
add_tail(theme_el, text)
parent[pos+1:pos+1] = content_els
+ pos_text = 'after'
else:
if pos == 0:
add_text(parent, text)
else:
add_tail(parent[pos-1], text)
parent[pos:pos] = content_els
+ pos_text = 'before'
+ if self.move:
+ for el in reversed(content_els):
+ move_tail_upwards(el)
+ el.getparent().remove(el)
+ verb = 'Moving'
+ log_text = ' and removing the now-empty content element(s)'
+ else:
+ verb = 'Copying'
+ log_text = ''
+ log.debug(self, '%s the children of content %s %s the theme element %s%s',
+ verb, self.format_tags(content_els), pos_text, self.format_tag(theme_el), log_text)
if theme_type == 'attributes':
## FIXME: handle named attributes
assert content_type == 'attributes'
if len(content_els) > 1:
if self.manycontent[0] == 'abort':
- log.debug(self, 'aborting because %s elements in the content match content="%s"',
- len(content_els), self.content)
+ log.debug(self, 'aborting because %i elements in the content (%s) match content="%s"',
+ len(content_els), self.format_tags(content_els), self.content)
raise AbortTheme()
else:
if self.manycontent[0] == 'warn':
log_meth = log.warn
else:
log_meth = log.debug
- log_meth(self, '%s elements match content="%s" (but only one expected), using the %s match',
- len(content_els, self.content, self.manycontent[1]))
+ log_meth(self, '%s elements match content="%s" (%s) but only one is expected, using the %s match',
+ len(content_els), self.content, self.format_tags(content_els), self.manycontent[1])
if self.manycontent[1] == 'first':
content_els = [content_els[0]]
else:
content_els = [content_els[-1]]
content_attrib = content_els[0].attrib
theme_attrib = theme_el.attrib
+ if self.move:
+ verb = 'Moved'
+ else:
+ verb = 'Copied'
if self._append:
if attributes:
+ avoided_attrs = []
+ copied_attrs = []
for name in attributes:
if name in content_attrib:
- theme_attrib.setdefault(name, content_attrib[name])
+ if name in theme_attrib:
+ avoided_attrs.append(name)
+ else:
+ theme_attrib[name] = content_attrib[name]
+ copied_attrs.append(name)
+ if avoided_attrs:
+ log.debug(self, '%s %s from the content element %s to the theme element %s, '
+ 'and did not copy the %s because they were already '
+ 'present in the theme element',
+ verb, self.format_attribute_names(copied_attrs), self.format_tag(content_els[0]),
+ self.format_tag(theme_el), self.format_attribute_names(avoided_attrs))
+ else:
+ log.debug(self, '%s %s from the content element %s to the theme '
+ 'element %s',
+ verb, self.format_attribute_names(copied_attrs), self.format_tag(content_els[0]),
+ self.format_tag(theme_el))
else:
+ avoided_attrs = []
+ copied_attrs = []
for key, value in content_attrib.items():
- theme_attrib.setdefault(key, value)
+ if key in theme_attrib:
+ avoided_attrs.append(key)
+ else:
+ copied_attrs.append(key)
+ theme_attrib[key] = value
+ if avoided_attrs:
+ log.debug(self, '%s %s from the content element %s to the theme element %s, '
+ 'and did not copy the %s because they were already present in the theme element',
+ verb, self.format_attribute_names(copied_attrs), self.format_tag(content_els[0]),
+ self.format_tag(theme_el), self.format_attribute_names(avoided_attrs))
+ else:
+ log.debug(self, '%s %s from the content element %s to the theme element %s',
+ verb, self.format_attribute_names(copied_attrs), self.format_tag(content_els[0]),
+ self.format_tab(theme_el))
else:
if attributes:
for name in attributes:
if name in content_attrib:
theme_attrib.set(name, content_attrib[name])
+ log.debug(self, '%s %s from the content element %s to the theme element %s',
+ verb, self.format_attribute_names(attributes), self.format_tag(content_els[0]), self.format_tag(theme_el))
else:
theme_attrib.update(content_attrib)
+ log.debug(self, '%s all the attributes from the content element %s to the theme element %s',
+ verb, self.format_tag(content_els[0]), self.format_tag(theme_el))
if self.move:
if attributes:
for name in attributes:
@@ -692,30 +850,43 @@
for el in els:
move_tail_upwards(el)
el.getparent().remove(el)
+ log.debug(self, 'Dropping %s %s', name, self.format_tags(els))
elif sel_type == 'children':
- el[:] = []
- el.text = ''
+ for el in els:
+ el[:] = []
+ el.text = ''
+ log.debug(self, 'Dropping the children of %s %s', name, self.format_tags(els))
elif sel_type == 'attributes':
- attrib = el.attrib
+ for el in els:
+ attrib = el.attrib
+ if attributes:
+ for name in attributes:
+ if name in attrib:
+ del attrib[name]
+ else:
+ attrib.clear()
if attributes:
- for name in attributes:
- if name in attrib:
- del attrib[name]
+ log.debug(self, 'Dropping the %s from the %s %s',
+ self.format_attribute_names(attributes), name, self.format_tags(els))
else:
- attrib.clear()
+ log.debug(self, 'Dropping all the attributes of %s %s',
+ name, self.format_tags(els))
elif sel_type == 'tag':
- children = list(el)
- if children:
- add_tail(children[-1], el.tail)
- else:
- add_text(el, el.tail)
- parent = el.getparent()
- pos = parent.index(el)
- if pos == 0:
- add_text(parent, el.text)
- else:
- add_tail(parent[pos-1], el.text)
- parent[pos:pos+1] = children
+ for el in els:
+ children = list(el)
+ if children:
+ add_tail(children[-1], el.tail)
+ else:
+ add_text(el, el.tail)
+ parent = el.getparent()
+ pos = parent.index(el)
+ if pos == 0:
+ add_text(parent, el.text)
+ else:
+ add_tail(parent[pos-1], el.text)
+ parent[pos:pos+1] = children
+ log.debug(self, 'Dropping the tag (flattening the element) of %s %s',
+ name, self.format_tags(els))
else:
assert 0
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py Tue Jul 1 22:21:15 2008
@@ -9,10 +9,12 @@
class RuleSet(object):
- def __init__(self, matchers, rules_by_class, default_theme=None):
+ def __init__(self, matchers, rules_by_class, default_theme=None,
+ source_location=None):
self.matchers = matchers
self.rules_by_class = rules_by_class
self.default_theme = default_theme
+ self.source_location = source_location
def apply_rules(self, req, resp, resource_fetcher, log):
extra_headers = parse_meta_headers(resp.body)
@@ -67,6 +69,14 @@
def parse_document(self, s, url):
return document_fromstring(s, base_url=url)
+ def log_description(self, log=None):
+ if log is None:
+ name = 'ruleset'
+ else:
+ name = 'ruleset' % log.link_to(self.source_location, source=True)
+ desc = '<%s>' % name
+ return desc
+
@classmethod
def parse_xml(cls, doc, source_location):
assert doc.tag == 'ruleset'
@@ -94,7 +104,8 @@
rules_by_class.setdefault(class_name, []).append(rule)
if default_theme:
default_theme = urlparse.urljoin(doc.base, default_theme)
- return cls(matchers, rules_by_class, default_theme=default_theme)
+ return cls(matchers, rules_by_class, default_theme=default_theme,
+ source_location=source_location)
_meta_tag_re = re.compile(r'', re.I | re.S)
_http_equiv_re = re.compile(r'http-equiv=(?:"([^"]*)"|([^\s>]*))', re.I|re.S)
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/rules.xml
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/rules.xml (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/rules.xml Tue Jul 1 22:21:15 2008
@@ -2,6 +2,6 @@
-
+
From ianb at codespeak.net Tue Jul 1 23:21:08 2008
From: ianb at codespeak.net (ianb at codespeak.net)
Date: Tue, 1 Jul 2008 23:21:08 +0200 (CEST)
Subject: [z3-checkins] r56220 -
z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance
Message-ID: <20080701212108.67A71169E21@codespeak.net>
Author: ianb
Date: Tue Jul 1 23:21:06 2008
New Revision: 56220
Modified:
z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/log.py
z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/middleware.py
z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py
Log:
Add some links to the log; remove an unnecessary log message
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/log.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/log.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/log.py Tue Jul 1 23:21:06 2008
@@ -55,6 +55,12 @@
log_template = HTMLTemplate('''\
{{if log.messages}}
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/middleware.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/middleware.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/middleware.py Wed Jul 2 02:57:25 2008
@@ -70,7 +70,7 @@
else:
## FIXME: pluggable subrequest handler?
subreq = Request.blank(url)
- resp = subreq.get_response(proxy_exact_request)
+ subresp = subreq.get_response(proxy_exact_request)
log.debug(self, 'External request for %s: %s content-type: %s',
url, subresp.status, subresp.content_type)
return subresp
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/pagematch.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/pagematch.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/pagematch.py Wed Jul 2 02:57:25 2008
@@ -33,7 +33,8 @@
"""
Creates an instance of Match from the given parsed XML element.
"""
- assert el.tag == 'match'
+ assert (el.tag == 'match'
+ or el.tag == 'rule')
classes = el.get('class', '').split()
abort = asbool(el.get('abort'))
if not abort and not classes:
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/rules.py Wed Jul 2 02:57:25 2008
@@ -6,10 +6,13 @@
from deliverance.exceptions import add_exception_info, DeliveranceSyntaxError
from deliverance.util.converters import asbool, html_quote
from deliverance.selector import Selector
+from deliverance.pagematch import Match
+from deliverance.themeref import Theme
from lxml import etree
from lxml.html import document_fromstring
from tempita import html
import copy
+import urlparse
CONTENT_ATTRIB = 'x-a-marker-attribute-for-deliverance'
@@ -18,13 +21,17 @@
This represents everything in a section.
"""
- def __init__(self, classes, actions, theme, suppress_standard, source_location):
+ def __init__(self, classes, actions, theme, match, suppress_standard, source_location):
self.classes = classes
self._actions = actions
self.theme = theme
+ self.match = match
self.suppress_standard = suppress_standard
self.source_location = source_location
+ match_attrs = set([
+ 'path', 'domain', 'request-header', 'response-header', 'environ'])
+
@classmethod
def parse_xml(cls, el, source_location):
"""
@@ -40,14 +47,27 @@
for el in el.iterchildren():
if el.tag == 'theme':
## FIXME: error if more than one theme
- ## FIXME: error if no href
- theme = el.get('href')
+ theme = Theme.parse_xml(el, source_location)
continue
if el.tag is etree.Comment:
continue
action = parse_action(el, source_location)
actions.append(action)
- return cls(classes, actions, theme, suppress_standard, source_location)
+ match = None
+ for attr in cls.match_attrs:
+ if el.get(attr):
+ match = Match.parse_xml(el, source_location)
+ if match.abort:
+ raise DeliveranceSyntaxError(
+ "You cannot have an abort attribute on elements",
+ element=el)
+ if match.last:
+ ## FIXME: is last a good alternative to suppress-standard?
+ raise DeliveranceSyntaxError(
+ "You cannot have a last attribute on elements",
+ element=el)
+ break
+ return cls(classes, actions, theme, match, suppress_standard, source_location)
def apply(self, content_doc, theme_doc, resource_fetcher, log):
"""
@@ -383,11 +403,15 @@
Applies this action to the theme_doc.
"""
if self.content_href:
- ## FIXME: check response type
- ## FIXME: Join content_href with request url?
- content_resp = resource_fetcher(self.content_href)
+ ## FIXME: Is this a weird way to resolve the href?
+ href = urlparse.urljoin(log.request.url, self.content_href)
+ content_resp = resource_fetcher(href)
log.debug(self, 'Fetching resource from href="%s": %s',
- self.content_href, content_resp.status)
+ href, content_resp.status)
+ if content_resp.status_int != 200:
+ log.warn(self, 'Resource %s returned the status %s; skipping rule',
+ href, content_resp.status)
+ return
content_doc = document_fromstring(content_resp.body, base_url=self.content_href)
if not self.if_content_matches(content_doc, log):
return
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/ruleset.py Wed Jul 2 02:57:25 2008
@@ -1,6 +1,7 @@
from deliverance.exceptions import AbortTheme, DeliveranceSyntaxError
from deliverance.pagematch import run_matches, Match
from deliverance.rules import Rule, remove_content_attribs
+from deliverance.themeref import Theme
from lxml.html import tostring, document_fromstring
from lxml.etree import XML
import re
@@ -26,6 +27,8 @@
classes = run_matches(self.matchers, req, response_headers, log)
except AbortTheme:
return resp
+ if 'X-Deliverance-Page-Class' in resp.headers:
+ classes.extend(resp.headers['X-Deliverance-Page-Class'].strip().split())
if not classes:
classes = ['default']
rules = []
@@ -42,10 +45,16 @@
theme = self.default_theme
## FIXME: error if not theme still
assert theme is not None
- theme_doc = self.get_theme(theme, resource_fetcher, log)
+ theme_href = theme.resolve_href(req, resp)
+ theme_doc = self.get_theme(theme_href, resource_fetcher, log)
content_doc = self.parse_document(resp.body, req.url)
run_standard = True
for rule in rules:
+ if rule.match is not None:
+ matches = rule.match(req, response_headers, log)
+ if not matches:
+ log.debug(rule, "Skipping ")
+ continue
rule.apply(content_doc, theme_doc, resource_fetcher, log)
if rule.suppress_standard:
run_standard = False
@@ -92,7 +101,7 @@
rules.append(rule)
elif el.tag == 'theme':
## FIXME: Add parse error
- default_theme = el.get('href')
+ default_theme = Theme.parse_xml(el, source_location)
else:
## FIXME: source location?
raise DeliveranceSyntaxError(
@@ -102,8 +111,8 @@
for rule in rules:
for class_name in rule.classes:
rules_by_class.setdefault(class_name, []).append(rule)
- if default_theme:
- default_theme = urlparse.urljoin(doc.base, default_theme)
+ if default_theme and default_theme.href:
+ default_theme.href = urlparse.urljoin(doc.base, default_theme.href)
return cls(matchers, rules_by_class, default_theme=default_theme,
source_location=source_location)
Modified: z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/index.html
==============================================================================
--- z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/index.html (original)
+++ z3/deliverance/sandbox/ianb/deliverance/trunk/deliverance/tests/example-files/index.html Wed Jul 2 02:57:25 2008
@@ -5,6 +5,20 @@
-This is my awesome site!
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin ullamcorper sem at orci. Nulla facilisi. Aenean bibendum suscipit diam. Maecenas vel pede vitae lorem tempor sollicitudin. Quisque pretium sapien eget dolor. In metus sem, facilisis in, laoreet in, porta luctus, odio. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam viverra felis quis mauris. Phasellus vel quam sed arcu ullamcorper bibendum. Phasellus posuere tellus pellentesque ligula. Suspendisse volutpat pede rhoncus nisi. Vivamus interdum odio eu odio. Nulla felis sapien, pretium sed, facilisis quis, fringilla et, leo. Suspendisse potenti. Praesent quis tellus. Aliquam cursus, ipsum in sollicitudin commodo, dui lacus varius ipsum, mattis fermentum sapien nisl sit amet mauris. Nullam molestie. In commodo viverra nulla. Curabitur vitae velit id erat pellentesque consectetuer. Nulla quis quam.
+
+
+Maecenas venenatis, lectus a luctus porttitor, ipsum pede pretium purus, et vehicula elit lacus a est. Proin ipsum magna, commodo sed, adipiscing et, imperdiet at, orci. Morbi molestie nisi in dui. Donec ultricies dui nec leo gravida lobortis. Donec lobortis dolor nec urna vulputate adipiscing. Vestibulum facilisis commodo est. Donec ornare, odio a rhoncus ornare, purus purus pellentesque dui, vitae iaculis dolor justo ultrices mauris. Donec eget libero. Ut tellus. Donec eget justo. In tempor augue sit amet ante. Donec vitae urna at eros vestibulum faucibus.
+
+
+Praesent quam. Curabitur nec turpis. Donec dictum. Proin pellentesque, diam luctus convallis fermentum, mi magna consectetuer sapien, quis semper arcu nunc eget arcu. Nunc suscipit semper nunc. Sed felis metus, adipiscing a, vulputate nec, dapibus vitae, diam. Sed tristique nisi in tellus. Ut odio ipsum, ultricies placerat, vehicula nec, tempor non, mi. Vestibulum tortor. Mauris mollis augue ut nunc. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Mauris lacus. Donec ac odio ac mi euismod feugiat. Duis mauris velit, porttitor aliquam, faucibus ac, hendrerit sit amet, orci. Pellentesque ultricies pede sodales metus. Vestibulum mauris. Phasellus non leo. In eu magna eu sapien molestie porttitor. Nunc malesuada bibendum ligula. Etiam sagittis tristique neque.
+
+
+Maecenas velit eros, accumsan porta, sodales in, ultrices auctor, felis. In metus nisi, mattis vel, bibendum id, placerat vel, elit. Vestibulum vestibulum. Sed auctor. Fusce lobortis. Vivamus porta cursus dui. Etiam neque tortor, egestas sed, ornare in, gravida eu, urna. Vestibulum a pede. In viverra sodales urna. Aliquam tempus molestie massa. Nullam congue viverra felis.
+
+
+Nunc lacus odio, lacinia a, mattis quis, aliquam in, lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed vel odio. Suspendisse ut est eu mauris auctor interdum. Sed et leo vel turpis condimentum elementum. Morbi neque mauris, sagittis eu, posuere id, molestie et, odio. Curabitur tortor. Curabitur eu pede vitae ipsum consequat porttitor. Nam at massa sed nulla lacinia sollicitudin. Donec dapibus. Duis felis justo, tincidunt sed, aliquet sit amet, rhoncus ut, neque. Duis id turpis at sem tincidunt adipiscing. Sed ante pede, facilisis a, sollicitudin sit amet, malesuada id, erat. Cras non purus.
+