Commit 6a43c2e7 authored by Andreas Jung's avatar Andreas Jung
parent 577af05e
......@@ -9,6 +9,12 @@ Zope Changes
Restructuring
- Removed deprecated ZTUtil.Iterator module
- Removed deprecated StructuredText module
- Removed deprecated TAL module
- Removed deprecated modules from Products.PageTemplates.
- Removed deprecated ZCML directives from Five including the whole
......
......@@ -261,16 +261,8 @@ class OrderSupport(object):
old_position = self.getObjectPosition(id)
result = super(OrderSupport, self).manage_renameObject(id, new_id,
REQUEST)
try:
self.moveObjectToPosition(new_id, old_position,
suppress_events=True)
except TypeError:
# BBB: removed in Zope 2.11
self.moveObjectToPosition(new_id, old_position)
warnings.warn(
"%s.moveObjectToPosition without suppress_events is "
"deprecated and will be removed in Zope 2.11." %
self.__class__.__name__, DeprecationWarning)
return result
def tpValues(self):
......
......@@ -504,15 +504,6 @@ class ZCatalog(Folder, Persistent, Implicit):
'?manage_tabs_message=Reindexing%20Performed')
# BBB: will be removed in Zope 2.12 (like TextIndex itself)
security.declareProtected(manage_zcatalog_entries, 'availableSplitters')
def availableSplitters(self):
""" splitter we can add """
# This import will trigger a deprecation warning about TextIndex
from Products.PluginIndexes.TextIndex import Splitter
return Splitter.availableSplitters
security.declareProtected(manage_zcatalog_entries, 'catalog_object')
def catalog_object(self, obj, uid=None, idxs=None, update_metadata=1, pghandler=None):
""" wrapper around catalog """
......
......@@ -14,7 +14,6 @@
"""ZCatalog product"""
import ZCatalog, CatalogAwareness, CatalogPathAwareness
from Products.PluginIndexes.TextIndex import Vocabulary
# BBB: ZClasses are deprecated but we don't want the warning to appear here
import warnings
......@@ -44,13 +43,6 @@ def initialize(context):
icon='www/ZCatalog.gif',
)
context.registerClass(
Vocabulary.Vocabulary,
permission='Add Vocabularies',
constructors=(Vocabulary.manage_addVocabularyForm,
Vocabulary.manage_addVocabulary),
icon='www/Vocabulary.gif',
)
context.registerHelp()
context.registerHelpTitle('Zope Help')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import re
from zope.structuredtext import stng as ST
from zope.structuredtext import stdom as STDOM
from zope.structuredtext.stletters import letters
from types import StringType, UnicodeType, ListType
StringTypes = (StringType, UnicodeType)
class StructuredTextExample(ST.StructuredTextParagraph):
"""Represents a section of document with literal text, as for examples"""
def __init__(self, subs, **kw):
t=[]; a=t.append
for s in subs: a(s.getNodeValue())
ST.StructuredTextParagraph.__init__(self, '\n\n'.join(t), (), **kw)
def getColorizableTexts(self): return ()
def setColorizableTexts(self, src): pass # never color examples
class StructuredTextBullet(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextNumbered(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextDescription(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, title, src, subs, **kw):
ST.StructuredTextParagraph.__init__(self, src, subs, **kw)
self._title=title
def getColorizableTexts(self): return self._title, self._src
def setColorizableTexts(self, src): self._title, self._src = src
def getChildren(self):
return (StructuredTextDescriptionTitle(self._title),
StructuredTextDescriptionBody(self._src, self._subs))
class StructuredTextSectionTitle(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
class StructuredTextSection(ST.StructuredTextParagraph):
"""Represents a section of a document with a title and a body"""
def __init__(self, src, subs=None, **kw):
ST.StructuredTextParagraph.__init__(
self, StructuredTextSectionTitle(src), subs, **kw)
def getColorizableTexts(self):
return self._src.getColorizableTexts()
def setColorizableTexts(self,src):
self._src.setColorizableTexts(src)
# a StructuredTextTable holds StructuredTextRows
class StructuredTextTable(ST.StructuredTextDocument):
"""
rows is a list of lists containing tuples, which
represent the columns/cells in each rows.
EX
rows = [[('row 1:column1',1)],[('row2:column1',1)]]
"""
def __init__(self, rows, src, subs, **kw):
ST.StructuredTextDocument.__init__(self, subs, **kw)
self._rows = []
for row in rows:
if row:
self._rows.append(StructuredTextRow(row,kw))
def getRows(self):
return [self._rows]
def _getRows(self):
return self.getRows()
def getColorizableTexts(self):
"""
return a tuple where each item is a column/cell's
contents. The tuple, result, will be of this format.
("r1 col1", "r1=col2", "r2 col1", "r2 col2")
"""
#result = ()
result = []
for row in self._rows:
for column in row.getColumns()[0]:
#result = result[:] + (column.getColorizableTexts(),)
result.append(column.getColorizableTexts()[0])
return result
def setColorizableTexts(self,texts):
"""
texts is going to a tuple where each item is the
result of being mapped to the colortext function.
Need to insert the results appropriately into the
individual columns/cells
"""
for row_index in range(len(self._rows)):
for column_index in range(len(self._rows[row_index]._columns)):
self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
texts = texts[1:]
def _getColorizableTexts(self):
return self.getColorizableTexts()
def _setColorizableTexts(self):
return self.setColorizableTexts()
# StructuredTextRow holds StructuredTextColumns
class StructuredTextRow(ST.StructuredTextDocument):
def __init__(self,row,kw):
"""
row is a list of tuples, where each tuple is
the raw text for a cell/column and the span
of that cell/column.
EX
[('this is column one',1), ('this is column two',1)]
"""
ST.StructuredTextDocument.__init__(self, [], **kw)
self._columns = []
for column in row:
self._columns.append(StructuredTextColumn(column[0],column[1],kw))
def getColumns(self):
return [self._columns]
def _getColumns(self):
return [self._columns]
# this holds the raw text of a table cell
class StructuredTextColumn(ST.StructuredTextParagraph):
"""
StructuredTextColumn is a cell/column in a table.
This contains the actual text of a column and is
thus a StructuredTextParagraph. A StructuredTextColumn
also holds the span of its column
"""
def __init__(self,text,span,kw):
ST.StructuredTextParagraph.__init__(self, text, [], **kw)
self._span = span
def getSpan(self):
return self._span
def _getSpan(self):
return self._span
class StructuredTextMarkup(STDOM.Element):
def __init__(self, v, **kw):
self._value=v
self._attributes=kw.keys()
for k, v in kw.items(): setattr(self, k, v)
def getChildren(self, type=type, lt=type([])):
v=self._value
if type(v) is not lt: v=[v]
return v
def getColorizableTexts(self): return self._value,
def setColorizableTexts(self, v): self._value=v[0]
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__, `self._value`)
class StructuredTextLiteral(StructuredTextMarkup):
def getColorizableTexts(self): return ()
def setColorizableTexts(self, v): pass
class StructuredTextEmphasis(StructuredTextMarkup): pass
class StructuredTextStrong(StructuredTextMarkup): pass
class StructuredTextInnerLink(StructuredTextMarkup): pass
class StructuredTextNamedLink(StructuredTextMarkup): pass
class StructuredTextUnderline(StructuredTextMarkup): pass
class StructuredTextLink(StructuredTextMarkup):
"A simple hyperlink"
class DocumentClass:
"""
Class instance calls [ex.=> x()] require a structured text
structure. Doc will then parse each paragraph in the structure
and will find the special structures within each paragraph.
Each special structure will be stored as an instance. Special
structures within another special structure are stored within
the 'top' structure
EX : '-underline this-' => would be turned into an underline
instance. '-underline **this**' would be stored as an underline
instance with a strong instance stored in its string
"""
paragraph_types = [
'doc_bullet',
'doc_numbered',
'doc_description',
'doc_header',
'doc_table',
]
text_types = [
'doc_href1',
'doc_href2',
'doc_strong',
'doc_emphasize',
'doc_literal',
'doc_inner_link',
'doc_named_link',
'doc_underline',
]
def __call__(self, doc):
if type(doc) in StringTypes:
doc=ST.StructuredText(doc)
doc.setSubparagraphs(self.color_paragraphs(
doc.getSubparagraphs()))
else:
doc=ST.StructuredTextDocument(self.color_paragraphs(
doc.getSubparagraphs()))
return doc
def parse(self, raw_string, text_type,
type=type, sts=StringTypes, lt=type([])):
"""
Parse accepts a raw_string, an expr to test the raw_string,
and the raw_string's subparagraphs.
Parse will continue to search through raw_string until
all instances of expr in raw_string are found.
If no instances of expr are found, raw_string is returned.
Otherwise a list of substrings and instances is returned
"""
tmp = [] # the list to be returned if raw_string is split
append=tmp.append
if type(text_type) in sts: text_type=getattr(self, text_type)
while 1:
t = text_type(raw_string)
if not t: break
#an instance of expr was found
t, start, end = t
if start: append(raw_string[0:start])
tt=type(t)
if tt in sts:
# if we get a string back, add it to text to be parsed
raw_string = t+raw_string[end:len(raw_string)]
else:
if tt is lt:
# is we get a list, append it's elements
tmp[len(tmp):]=t
else:
# normal case, an object
append(t)
raw_string = raw_string[end:len(raw_string)]
if not tmp: return raw_string # nothing found
if raw_string: append(raw_string)
elif len(tmp)==1: return tmp[0]
return tmp
def color_text(self, str, types=None):
"""Search the paragraph for each special structure
"""
if types is None: types=self.text_types
for text_type in types:
if type(str) in StringTypes:
str = self.parse(str, text_type)
elif type(str) is ListType:
r=[]; a=r.append
for s in str:
if type(s) in StringTypes:
s=self.parse(s, text_type)
if type(s) is ListType: r[len(r):]=s
else: a(s)
else:
s.setColorizableTexts(
map(self.color_text,
s.getColorizableTexts()
))
a(s)
str=r
else:
r=[]; a=r.append; color=self.color_text
for s in str.getColorizableTexts():
color(s, (text_type,))
a(s)
str.setColorizableTexts(r)
return str
def color_paragraphs(self, raw_paragraphs,
type=type, sequence_types=(type([]), type(())),
sts=StringTypes):
result=[]
for paragraph in raw_paragraphs:
if paragraph.getNodeName() != 'StructuredTextParagraph':
result.append(paragraph)
continue
for pt in self.paragraph_types:
if type(pt) in sts:
# grab the corresponding function
pt=getattr(self, pt)
# evaluate the paragraph
r=pt(paragraph)
if r:
if type(r) not in sequence_types:
r=r,
new_paragraphs=r
for paragraph in new_paragraphs:
paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
break
else:
new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
self.color_paragraphs(paragraph.getSubparagraphs()),
indent=paragraph.indent),
# color the inline StructuredText types
# for each StructuredTextParagraph
for paragraph in new_paragraphs:
paragraph.setColorizableTexts(
map(self.color_text,
paragraph.getColorizableTexts()
))
result.append(paragraph)
return result
def doc_table(self,paragraph, expr = re.compile('(\s*)([||]+)').match):
#print "paragraph=>", type(paragraph), paragraph, paragraph._src
text = paragraph.getColorizableTexts()[0]
m = expr(text)
if not (m):
return None
rows = []
# initial split
for row in text.split("\n"):
rows.append(row)
# clean up the rows
for index in range(len(rows)):
tmp = []
rows[index] = rows[index].strip()
l = len(rows[index])-2
result = rows[index][:l].split("||")
for text in result:
if text:
tmp.append(text)
tmp.append('')
else:
tmp.append(text)
rows[index] = tmp
# remove trailing '''s
for index in range(len(rows)):
l = len(rows[index])-1
rows[index] = rows[index][:l]
result = []
for row in rows:
cspan = 0
tmp = []
for item in row:
if item:
tmp.append((item,cspan))
cspan = 0
else:
cspan = cspan + 1
result.append(tmp)
subs = paragraph.getSubparagraphs()
# indent=paragraph.indent
return StructuredTextTable(result,text,subs,indent=paragraph.indent)
def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m:
return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextBullet(top[m.span()[1]:], subs,
indent=paragraph.indent,
bullet=top[:m.span()[1]]
)
def doc_numbered(
self, paragraph,
expr = re.compile('(\s*[%s]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):
# This is the old expression. It had a nasty habit
# of grabbing paragraphs that began with a single
# letter word even if there was no following period.
#expr = re.compile('\s*'
# '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
# '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
# '\s+').match):
top=paragraph.getColorizableTexts()[0]
m=expr(top)
if not m: return None
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextNumbered(top[m.span()[1]:], subs,
indent=paragraph.indent,
number=top[:m.span()[1]])
def doc_description(
self, paragraph,
delim = re.compile('\s+--\s+').search,
nb=re.compile(r'[^\000- ]').search,
):
top=paragraph.getColorizableTexts()[0]
d=delim(top)
if not d: return None
start, end = d.span()
title=top[:start]
if title.find('\n') >= 0: return None
if not nb(title): return None
d=top[start:end]
top=top[end:]
subs=paragraph.getSubparagraphs()
if top[-2:]=='::':
subs=[StructuredTextExample(subs)]
top=top[:-1]
return StructuredTextDescription(
title, top, subs,
indent=paragraph.indent,
delim=d)
def doc_header(self, paragraph,
expr = re.compile('[ %s0-9.:/,-_*<>\?\'\"]+' % letters).match
):
subs=paragraph.getSubparagraphs()
if not subs: return None
top=paragraph.getColorizableTexts()[0]
if not top.strip(): return None
if top[-2:]=='::':
subs=StructuredTextExample(subs)
if top.strip()=='::': return subs
return ST.StructuredTextParagraph(top[:-1],
[subs],
indent=paragraph.indent,
level=paragraph.level)
if top.find('\n') >= 0: return None
return StructuredTextSection(top, subs, indent=paragraph.indent, level=paragraph.level)
def doc_literal(
self, s,
expr=re.compile(
"(?:\s|^)'" # open
"([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
"'(?:\s|[,.;:!?]|$)" # close
).search):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextLiteral(s[start:end]), start-1, end+1)
else:
return None
def doc_emphasize(
self, s,
expr = re.compile('\s*\*([ \n%s0-9.:/;,\'\"\?\=\-\>\<\(\)]+)\*(?!\*|-)' % letters).search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
else:
return None
def doc_inner_link(self,
s,
expr1 = re.compile("\.\.\s*").search,
expr2 = re.compile("\[[%s0-9]+\]" % letters).search):
# make sure we dont grab a named link
if expr2(s) and expr1(s):
start1,end1 = expr1(s).span()
start2,end2 = expr2(s).span()
if end1 == start2:
# uh-oh, looks like a named link
return None
else:
# the .. is somewhere else, ignore it
return (StructuredTextInnerLink(s[start2+1,end2-1]),start2,end2)
return None
elif expr2(s) and not expr1(s):
start,end = expr2(s).span()
return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
return None
def doc_named_link(self,
s,
expr=re.compile(r"(\.\.\s)(\[[%s0-9]+\])" % letters).search):
result = expr(s)
if result:
start,end = result.span(2)
str = s[start+1:end-1]
st,en = result.span()
return (StructuredTextNamedLink(str),st,en)
return None
def doc_underline(self,
s,
expr=re.compile("_([%s0-9\s\.,\?\/]+)_" % letters).search):
result = expr(s)
if result:
start,end = result.span(1)
st,e = result.span()
return (StructuredTextUnderline(s[start:end]),st,e)
else:
return None
def doc_strong(self,
s,
expr = re.compile('\s*\*\*([ \n%s0-9.:/;\-,!\?\'\"]+)\*\*' % letters).search
):
r=expr(s)
if r:
start, end = r.span(1)
return (StructuredTextStrong(s[start:end]), start-2, end+2)
else:
return None
def doc_href1(self, s,
expr=re.compile("(\"[ %s0-9\n\-\.\,\;\(\)\/\:\/\*\']+\")(:)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)([,]*\s*)" % letters).search
):
return self.doc_href(s, expr)
def doc_href2(self, s,
expr=re.compile('(\"[ %s0-9\n\-\.\:\;\(\)\/\*\']+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#\~]+)(\s*)' % letters).search
):
return self.doc_href(s, expr)
def doc_href(self, s, expr, punctuation = re.compile("[\,\.\?\!\;]+").match):
r=expr(s)
if r:
# need to grab the href part and the
# beginning part
start,e = r.span(1)
name = s[start:e]
name = name.replace('"','',2)
#start = start + 1
st,end = r.span(3)
if punctuation(s[end-1:end]):
end = end -1
link = s[st:end]
#end = end - 1
# name is the href title, link is the target
# of the href
return (StructuredTextLink(name, href=link),
start, end)
#return (StructuredTextLink(s[start:end], href=s[start:end]),
# start, end)
else:
return None
#! /usr/bin/env python -- # -*- python -*-
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Structured Text Manipulation
Parse a structured text string into a form that can be used with
structured formats, like html.
Structured text is text that uses indentation and simple
symbology to indicate the structure of a document.
A structured string consists of a sequence of paragraphs separated by
one or more blank lines. Each paragraph has a level which is defined
as the minimum indentation of the paragraph. A paragraph is a
sub-paragraph of another paragraph if the other paragraph is the last
preceding paragraph that has a lower level.
Special symbology is used to indicate special constructs:
- A single-line paragraph whose immediately succeeding paragraphs are lower
level is treated as a header.
- A paragraph that begins with a '-', '*', or 'o' is treated as an
unordered list (bullet) element.
- A paragraph that begins with a sequence of digits followed by a
white-space character is treated as an ordered list element.
- A paragraph that begins with a sequence of sequences, where each
sequence is a sequence of digits or a sequence of letters followed
by a period, is treated as an ordered list element.
- A paragraph with a first line that contains some text, followed by
some white-space and '--' is treated as
a descriptive list element. The leading text is treated as the
element title.
- Sub-paragraphs of a paragraph that ends in the word 'example' or the
word 'examples', or '::' is treated as example code and is output as is.
- Text enclosed single quotes (with white-space to the left of the
first quote and whitespace or punctuation to the right of the second quote)
is treated as example code.
- Text surrounded by '*' characters (with white-space to the left of the
first '*' and whitespace or punctuation to the right of the second '*')
is emphasized.
- Text surrounded by '**' characters (with white-space to the left of the
first '**' and whitespace or punctuation to the right of the second '**')
is made strong.
- Text surrounded by '_' underscore characters (with whitespace to the left
and whitespace or punctuation to the right) is made underlined.
- Text encloded by double quotes followed by a colon, a URL, and concluded
by punctuation plus white space, *or* just white space, is treated as a
hyper link. For example:
"Zope":http://www.zope.org/ is ...
Is interpreted as '<a href="http://www.zope.org/">Zope</a> is ....'
Note: This works for relative as well as absolute URLs.
- Text enclosed by double quotes followed by a comma, one or more spaces,
an absolute URL and concluded by punctuation plus white space, or just
white space, is treated as a hyper link. For example:
"mail me", mailto:amos@digicool.com.
Is interpreted as '<a href="mailto:amos@digicool.com">mail me</a>.'
- Text enclosed in brackets which consists only of letters, digits,
underscores and dashes is treated as hyper links within the document.
For example:
As demonstrated by Smith [12] this technique is quite effective.
Is interpreted as '... by Smith <a href="#12">[12]</a> this ...'. Together
with the next rule this allows easy coding of references or end notes.
- Text enclosed in brackets which is preceded by the start of a line, two
periods and a space is treated as a named link. For example:
.. [12] "Effective Techniques" Smith, Joe ...
Is interpreted as '<a name="12">[12]</a> "Effective Techniques" ...'.
Together with the previous rule this allows easy coding of references or
end notes.
- A paragraph that has blocks of text enclosed in '||' is treated as a
table. The text blocks correspond to table cells and table rows are
denoted by newlines. By default the cells are center aligned. A cell
can span more than one column by preceding a block of text with an
equivalent number of cell separators '||'. Newlines and '|' cannot
be a part of the cell text. For example:
|||| **Ingredients** ||
|| *Name* || *Amount* ||
||Spam||10||
||Eggs||3||
is interpreted as::
<TABLE BORDER=1 CELLPADDING=2>
<TR>
<TD ALIGN=CENTER COLSPAN=2> <strong>Ingredients</strong> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1> <em>Name</em> </TD>
<TD ALIGN=CENTER COLSPAN=1> <em>Amount</em> </TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Spam</TD>
<TD ALIGN=CENTER COLSPAN=1>10</TD>
</TR>
<TR>
<TD ALIGN=CENTER COLSPAN=1>Eggs</TD>
<TD ALIGN=CENTER COLSPAN=1>3</TD>
</TR>
</TABLE>
'''
import warnings
warnings.warn('The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext instead.',
DeprecationWarning, stacklevel=2)
import ts_regex
import string, re
def untabify(aString,
indent_tab=ts_regex.compile('\(\n\|^\)\( *\)\t').search_group,
):
'''\
Convert indentation tabs to spaces.
'''
result=''
rest=aString
while 1:
ts_results = indent_tab(rest, (1,2))
if ts_results:
start, grps = ts_results
lnl=len(grps[0])
indent=len(grps[1])
result=result+rest[:start]
rest="\n%s%s" % (' ' * ((indent/8+1)*8),
rest[start+indent+1+lnl:])
else:
return result+rest
def indent(aString, indent=2):
"""Indent a string the given number of spaces"""
r=untabify(aString).split('\n')
if not r: return ''
if not r[-1]: del r[-1]
tab=' '*indent
return "%s%s\n" % (tab,('\n'+tab).join(r))
def reindent(aString, indent=2, already_untabified=0):
"reindent a block of text, so that the minimum indent is as given"
if not already_untabified: aString=untabify(aString)
l=indent_level(aString)[0]
if indent==l: return aString
r=[]
append=r.append
if indent > l:
tab=' ' * (indent-l)
for s in aString.split('\n'): append(tab+s)
else:
l=l-indent
for s in aString.split('\n'): append(s[l:])
return '\n'.join(r)
def indent_level(aString,
indent_space=ts_regex.compile('\n\( *\)').search_group,
):
'''\
Find the minimum indentation for a string, not counting blank lines.
'''
start=0
text='\n'+aString
indent=l=len(text)
while 1:
ts_results = indent_space(text, (1,2), start)
if ts_results:
start, grps = ts_results
i=len(grps[0])
start=start+i+1
if start < l and text[start] != '\n': # Skip blank lines
if not i: return (0,aString)
if i < indent: indent = i
else:
return (indent,aString)
def paragraphs(list,start):
l=len(list)
level=list[start][0]
i=start+1
while i < l and list[i][0] > level: i=i+1
return i-1-start
def structure(list):
if not list: return []
i=0
l=len(list)
r=[]
while i < l:
sublen=paragraphs(list,i)
i=i+1
r.append((list[i-1][1],structure(list[i:i+sublen])))
i=i+sublen
return r
class Table:
CELL=' <td align="center" colspan="%i">%s</td>\n'
ROW=' <tr>\n%s </tr>\n'
TABLE='\n<table border="1" cellpadding="2">\n%s</table>'
def create(self,aPar,
td_reg=re.compile(r'[ \t\n]*\|\|([^\0|]*)')
):
'''parses a table and returns nested list representing the
table'''
self.table=[]
text=filter(None,aPar.split('\n'))
for line in text:
row=[]
while 1:
mo = td_reg.match(line)
if not mo: return 0
pos = mo.end(1)
row.append(mo.group(1))
if pos==len(line):break
line=line[pos:]
self.table.append(row)
return 1
def html(self):
'''Creates an HTML representation of table'''
htmltable=[]
for row in self.table:
htmlrow=[]
colspan=1
for cell in row:
if cell=='':
colspan=colspan+1
continue
else:
htmlrow.append(self.CELL%(colspan,cell))
colspan=1
htmltable.append(self.ROW % ''.join(htmlrow))
return self.TABLE % ''.join(htmltable)
table=Table()
class StructuredText:
"""Model text as structured collection of paragraphs.
Structure is implied by the indentation level.
This class is intended as a base classes that do actual text
output formatting.
"""
def __init__(self, aStructuredString, level=0,
paragraph_divider=ts_regex.compile('\(\r?\n *\)+\r?\n'),
):
'''Convert a structured text string into a structured text object.
Aguments:
aStructuredString -- The string to be parsed.
level -- The level of top level headings to be created.
'''
pat = ' \"([%s0-9-_,./?=@~&]*)\":' % string.letters+ \
'([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
'([.:?;] )'
p_reg = re.compile(pat,re.M)
aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)
pat = ' \"([%s0-9-_,./?=@~&]*)\", ' % string.letters+ \
'([-:%s0-9_,./?=@#~&]*?)' % string.letters + \
'([.:?;] )'
p_reg = re.compile(pat,re.M)
aStructuredString = p_reg.sub(r'<a href="\2">\1</a>\3 ' , aStructuredString)
protoless = aStructuredString.find('<a href=":')
if protoless != -1:
aStructuredString = re.sub('<a href=":', '<a href="',
aStructuredString)
self.level=level
paragraphs=ts_regex.split(untabify(aStructuredString),
paragraph_divider)
paragraphs=map(indent_level,paragraphs)
self.structure=structure(paragraphs)
def __str__(self):
return str(self.structure)
ctag_prefix=r'([\x00- \\(]|^)'
ctag_suffix=r'([\x00- ,.:;!?\\)]|$)'
ctag_middle=r'[%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s]'
ctag_middl2=r'[%s][%s]([^\x00- %s][^%s]*[^\x00- %s]|[^%s])[%s][%s]'
def ctag(s,
em=re.compile(
ctag_prefix+(ctag_middle % (("*",)*6) )+ctag_suffix),
strong=re.compile(
ctag_prefix+(ctag_middl2 % (("*",)*8))+ctag_suffix),
under=re.compile(
ctag_prefix+(ctag_middle % (("_",)*6) )+ctag_suffix),
code=re.compile(
ctag_prefix+(ctag_middle % (("\'",)*6))+ctag_suffix),
):
if s is None: s=''
s=strong.sub(r'\1<strong>\2</strong>\3',s)
s=under.sub( r'\1<u>\2</u>\3',s)
s=code.sub( r'\1<code>\2</code>\3',s)
s=em.sub( r'\1<em>\2</em>\3',s)
return s
class HTML(StructuredText):
'''\
An HTML structured text formatter.
'''\
def __str__(self,
extra_dl=re.compile("</dl>\n<dl>"),
extra_ul=re.compile("</ul>\n<ul>"),
extra_ol=re.compile("</ol>\n<ol>"),
):
'''\
Return an HTML string representation of the structured text data.
'''
s=self._str(self.structure,self.level)
s=extra_dl.sub('\n',s)
s=extra_ul.sub('\n',s)
s=extra_ol.sub('\n',s)
return s
def ul(self, before, p, after):
if p: p="<p>%s</p>" % ctag(p).strip()
return ('%s<ul><li>%s\n%s\n</li></ul>\n'
% (before,p,after))
def ol(self, before, p, after):
if p: p="<p>%s</p>" % ctag(p).strip()
return ('%s<ol><li>%s\n%s\n</li></ol>\n'
% (before,p,after))
def dl(self, before, t, d, after):
return ('%s<dl><dt>%s</dt><dd><p>%s</p>\n%s\n</dd></dl>\n'
% (before,ctag(t),ctag(d),after))
def head(self, before, t, level, d):
if level > 0 and level < 6:
return ('%s<h%d>%s</h%d>\n%s\n'
% (before,level,ctag(t).strip(),level,d))
t="<p><strong>%s</strong></p>" % ctag(t).strip()
return ('%s<dl><dt>%s\n</dt><dd>%s\n</dd></dl>\n'
% (before,t,d))
def normal(self,before,p,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(p),after)
def pre(self,structure,tagged=0):
if not structure: return ''
if tagged:
r=''
else:
r='<pre>\n'
for s in structure:
r="%s%s\n\n%s" % (r,html_quote(s[0]),self.pre(s[1],1))
if not tagged: r=r.rstrip()+'\n</pre>\n'
return r
def table(self,before,table,after):
return '%s<p>%s</p>\n%s\n' % (before,ctag(table),after)
def _str(self,structure,level,
# Static
bullet=ts_regex.compile('[ \t\n]*[o*-][ \t\n]+\([^\0]*\)'
).match_group,
example=ts_regex.compile('[\0- ]examples?:[\0- ]*$'
).search,
dl=ts_regex.compile('\([^\n]+\)[ \t]+--[ \t\n]+\([^\0]*\)'
).match_group,
nl=ts_regex.compile('\n').search,
ol=ts_regex.compile(
'[ \t]*\(\([0-9]+\|[%s]+\)[.)]\)+[ \t\n]+\([^\0]*\|$\)' % string.letters
).match_group,
olp=ts_regex.compile('[ \t]*([0-9]+)[ \t\n]+\([^\0]*\|$\)'
).match_group,
):
r=''
for s in structure:
ts_results = bullet(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ul(r,p,ps)
continue
ts_results = ol(s[0], (3,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = olp(s[0], (1,))
if ts_results:
p = ts_results[1]
if s[0][-2:]=='::' and s[1]: ps=self.pre(s[1])
else: ps=self._str(s[1],level)
r=self.ol(r,p,ps)
continue
ts_results = dl(s[0], (1,2))
if ts_results:
t,d = ts_results[1]
r=self.dl(r,t,d,self._str(s[1],level))
continue
if example(s[0]) >= 0 and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0],self.pre(s[1]))
continue
if s[0][-2:]=='::' and s[1]:
# Introduce an example, using pre tags:
r=self.normal(r,s[0][:-1],self.pre(s[1]))
continue
if table.create(s[0]):
## table support.
r=self.table(r,table.html(),self._str(s[1],level))
continue
else:
if nl(s[0]) < 0 and s[1] and s[0][-1:] != ':':
# Treat as a heading
t=s[0]
r=self.head(r,t,level,
self._str(s[1],level and level+1))
else:
r=self.normal(r,s[0],self._str(s[1],level))
return r
def html_quote(v,
character_entities=(
(re.compile('&'), '&amp;'),
(re.compile("<"), '&lt;' ),
(re.compile(">"), '&gt;' ),
(re.compile('"'), '&quot;')
)): #"
text=str(v)
for re,name in character_entities:
text=re.sub(name,text)
return text
def html_with_references(text, level=1):
text = re.sub(
r'[\0\n]\.\. \[([0-9_%s-]+)\]' % string.letters,
r'\n <a name="\1">[\1]</a>',
text)
text = re.sub(
r'([\x00- ,])\[(?P<ref>[0-9_%s-]+)\]([\x00- ,.:])' % string.letters,
r'\1<a href="#\2">[\2]</a>\3',
text)
text = re.sub(
r'([\0- ,])\[([^]]+)\.html\]([\0- ,.:])',
r'\1<a href="\2.html">[\2]</a>\3',
text)
return HTML(text,level=level)
def main():
import sys, getopt
opts,args=getopt.getopt(sys.argv[1:],'twl')
if args:
[infile]=args
s=open(infile,'r').read()
else:
s=sys.stdin.read()
if opts:
if filter(lambda o: o[0]=='-w', opts):
print 'Content-Type: text/html\n'
if filter(lambda o: o[0]=='-l', opts):
import locale
locale.setlocale(locale.LC_ALL,"")
if s[:2]=='#!':
s=re.sub('^#![^\n]+','',s)
mo = re.compile('([\0-\n]*\n)').match(s)
if mo is not None:
s = s[len(mo.group(0)) :]
s=str(html_with_references(s))
if s[:4]=='<h1>':
t=s[4: s.find('</h1>')]
s='''<html><head><title>%s</title>
</head><body>
%s
</body></html>
''' % (t,s)
print s
else:
print html_with_references(s)
if __name__=="__main__": main()
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.docbook import DocBook as DocBookClass
from zope.structuredtext.docbook import \
DocBookChapter, DocBookChapterWithFigures, DocBookArticle, DocBookBook
from zope.deprecation import deprecated
deprecated("DocBookClass",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.docbook.DocBook '
'instead.')
deprecated("DocBookChapter",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.docbook.DocBookChapter '
'instead.')
deprecated("DocBookChapterWithFigures",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.docbook.DocBookChapterWithFigures '
'instead.')
deprecated("DocBookArticle",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.docbook.DocBookArticle '
'instead.')
deprecated("DocBookBook",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.docbook.DocBookBook '
'instead.')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.document import Document as DocumentClass
from zope.structuredtext.stng import (
StructuredTextExample, StructuredTextBullet, StructuredTextNumbered,
StructuredTextDescriptionTitle, StructuredTextDescriptionBody,
StructuredTextDescription, StructuredTextSectionTitle,
StructuredTextSection, StructuredTextTable, StructuredTextRow,
StructuredTextColumn, StructuredTextTableHeader,
StructuredTextTableData, StructuredTextMarkup, StructuredTextLiteral,
StructuredTextEmphasis, StructuredTextStrong, StructuredTextInnerLink,
StructuredTextNamedLink, StructuredTextUnderline, StructuredTextSGML,
StructuredTextLink, StructuredTextXref
)
from zope.deprecation import deprecated
deprecated("DocumentClass",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.document.Document '
'instead.')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.stng import StructuredTextImage
from zope.structuredtext.document import DocumentWithImages
from zope.deprecation import deprecated
deprecated("StructuredTextImage",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stng.StructuredTextImage '
'instead.')
deprecated("DocumentWithImages",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.document.DocumentWithImages '
'instead.')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.html import HTML as HTMLClass
from zope.deprecation import deprecated
deprecated("HTMLClass",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.html.HTML instead.')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.html import HTMLWithImages
from zope.deprecation import deprecated
deprecated("HTMLWithImages",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.html.HTMLWithImages '
'instead.')
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext.stng import \
indention, insert, display, display2, findlevel
from zope.structuredtext.stng import structurize as StructuredText
from zope.structuredtext.stng import \
StructuredTextParagraph, StructuredTextDocument
Basic = StructuredText
from zope.deprecation import deprecated
deprecated(("StructuredText", "Basic"),
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stng.structurize '
'instead.')
deprecated("StructuredTextParagraph",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stng.StructuredTextParagraph '
'instead.')
deprecated("StructuredTextDocument",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stng.StructuredTextDocument '
'instead.')
Using Structured Text
WARNING! The 'StructuredText' package has been deprecated and will
be removed in Zope 2.12. Use 'zope.structuredtext' instead.
The goal of StructuredText is to make it possible to express
structured text using a relatively simple plain text format. Simple
structures, like bullets or headings are indicated through
conventions that are natural, for some definition of
"natural". Hierarchical structures are indicated through
indentation. The use of indentation to express hierarchical
structure is inspired by the Python programming language.
Use of StructuredText consists of one to three logical steps. In the
first step, a text string is converted to a network of objects using
the 'StructuredText.Basic' facility, as in the following
example::
raw=open("mydocument.txt").read()
import StructuredText
st=StructuredText.Basic(raw)
The output of 'StructuredText.Basic' is simply a
StructuredTextDocument object containing StructuredTextParagraph
objects arranged in a hierarchy. Paragraphs are delimited by strings
of two or more whitespace characters beginning and ending with
newline characters. Hierarchy is indicated by indentation. The
indentation of a paragraph is the minimum number of leading spaces
in a line containing non-white-space characters after converting tab
characters to spaces (assuming a tab stop every eight characters).
StructuredTextNode objects support the read-only subset of the
Document Object Model (DOM) API. It should be possible to process
'StructuredTextNode' hierarchies using XML tools such as XSLT.
The second step in using StructuredText is to apply additional
structuring rules based on text content. A variety of differentText
rules can be used. Typically, these are used to implement a
structured text language for producing documents, but any sort of
structured text language could be implemented in the second
step. For example, it is possible to use StructuredText to implement
structured text formats for representing structured data. The second
step, which could consist of multiple processing steps, is
performed by processing, or "coloring", the hierarchy of generic
StructuredTextParagraph objects into a network of more specialized
objects. Typically, the objects produced should also implement the DOM
API to allow processing with XML tools.
A document processor is provided to convert a StructuredTextDocument
object containing only StructuredStructuredTextParagraph objects
into a StructuredTextDocument object containing a richer collection
of objects such as bullets, headings, emphasis, and so on using
hints in the text. Hints are selected based on conventions of the
sort typically seen in electronic mail or news-group postings. It
should be noted, however, that these conventions are somewhat
culturally dependent, fortunately, the document processor is easily
customized to implement alternative rules. Here's an example of
using the DOC processor to convert the output of the previous example::
doc=StructuredText.Document(st)
The final step is to process the colored networks produced from the
second step to produce additional outputs. The final step could be
performed by Python programs, or by XML tools. A Python outputter is
provided for the document processor output that produces Hypertext Markup
Language (HTML) text::
html=StructuredText.HTML(doc)
Customizing the document processor
The document processor is driven by two tables. The first table,
named 'paragraph_types', is a sequence of callable objects or method
names for coloring paragraphs. If a table entry is a string, then it
is the name of a method of the document processor to be used. For
each input paragraph, the objects in the table are called until one
returns a value (not 'None'). The value returned replaces the
original input paragraph in the output. If none of the objects in
the paragraph types table return a value, then a copy of the
original paragraph is used. The new object returned by calling a
paragraph type should implement the ReadOnlyDOM,
StructuredTextColorizable, and StructuredTextSubparagraphContainer
interfaces. See the 'Document.py' source file for examples.
A paragraph type may return a list or tuple of replacement
paragraphs, this allowing a paragraph to be split into multiple
paragraphs.
The second table, 'text_types', is a sequence of callable objects or
method names for coloring text. The callable objects in this table
are used in sequence to transform the input text into new text or
objects. The callable objects are passed a string and return
nothing ('None') or a three-element tuple consisting of:
- a replacement object,
- a starting position, and
- an ending position
The text from the starting position is (logically) replaced with the
replacement object. The replacement object is typically an object
that implements that implements the ReadOnlyDOM, and
StructuredTextColorizable interfaces. The replacement object can
also be a string or a list of strings or objects. Replacement is
done from beginning to end and text after the replacement ending
position will be passed to the character type objects for processing.
Example: adding wiki links
We want to add support for Wiki links. A Wiki link is a string of
text containing mixed-case letters, such that at least two of the
letters are upper case and such that the first letter is upper case.
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Alias module for StructuredTextClassic compatibility which makes
use of StructuredTextNG"""
import re
from zope.structuredtext import stx2html as HTML
from zope.structuredtext import stx2htmlWithReferences as html_with_references
StructuredText = HTML
from zope.deprecation import deprecated
deprecated(("HTML, StructuredText"),
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stx2html instead.')
deprecated("html_with_references",
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext.stx2htmlWithReferences '
'instead.')
def html_quote(v,
character_entities=(
(re.compile('&'), '&amp;'),
(re.compile("<"), '&lt;' ),
(re.compile(">"), '&gt;' ),
(re.compile('"'), '&quot;')
)): #"
text=str(v)
for re,name in character_entities:
text=re.sub(name,text)
return text
if __name__=='__main__':
import getopt
opts,args = getopt.getopt(sys.argv[1:],'',[])
for k,v in opts:
pass
for f in args:
print HTML(open(f).read())
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.structuredtext import html, document, docbook
from zope.structuredtext.stng import structurize as Basic
from StructuredText import html_quote
from zope.structuredtext import stx2html as HTML
from zope.structuredtext import stx2htmlWithReferences as html_with_references
from types import StringType, UnicodeType
# BBB -- 2006/01/08 -- Remove in Zope 2.12
import sys
import zope.structuredtext.stletters
import zope.structuredtext.stdom
sys.modules['StructuredText.STletters'] = zope.structuredtext.stletters
sys.modules['StructuredText.STDOM'] = zope.structuredtext.stdom
import ClassicDocumentClass
Classic = ClassicDocumentClass.DocumentClass()
Document = document.Document()
DocumentWithImages = document.DocumentWithImages()
HTMLWithImages = html.HTMLWithImages()
ClassicHTML = html.HTML
HTMLNG = html.HTML()
DocBookBook = docbook.DocBookBook()
DocBookChapter = docbook.DocBookChapter()
DocBookChapterWithFigures = docbook.DocBookChapterWithFigures()
DocBookArticle = docbook.DocBookArticle()
def HTML(src, level=1):
import warnings
warnings.warn(
'The StructuredText package is deprecated and will be removed '
'in Zope 2.12. Use zope.structuredtext instead.',
DeprecationWarning, stacklevel=2)
if isinstance(src, basestring):
return ClassicHTML(src, level)
return HTMLNG(src, level)
<html>
<head>
<title>Acquisition</title>
</head>
<body>
<h1>Acquisition</h1>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<p> Acquisition <a href="#ref1">[1]</a> is a mechanism that allows objects to obtain
attributes from their environment. It is similar to inheritence,
except that, rather than traversing an inheritence hierarchy
to obtain attributes, a containment hierarchy is traversed.</p>
<p> The <a href="ExtensionClass.html">ExtensionClass</a>. release includes mix-in
extension base classes that can be used to add acquisition as a
feature to extension subclasses. These mix-in classes use the
context-wrapping feature of ExtensionClasses to implement
acquisition. Consider the following example:
<pre>
import ExtensionClass, Acquisition
class C(ExtensionClass.Base):
color='red'
class A(Acquisition.Implicit):
def report(self):
print self.color
a=A()
c=C()
c.a=A()
c.a.report() # prints 'red'
d=C()
d.color='green'
d.a=a
d.a.report() # prints 'green'
a.report() # raises an attribute error
</pre>
</p>
<p> The class <code>A</code> inherits acquisition behavior from
<code>Acquisition.Implicit</code>. The object, <code>a</code>, "has" the color of
objects <code>c</code> and <code>d</code> when it is accessed through them, but it
has no color by itself. The object <code>a</code> obtains attributes
from it's environment, where it's environment is defined by
the access path used to reach <code>a</code>.</p>
<h2> Acquisition wrappers</h2>
<p> When an object that supports acquisition is accessed through
an extension class instance, a special object, called an
acquisition wrapper, is returned. In the example above, the
expression <code>c.a</code> returns an acquisition wrapper that
contains references to both <code>c</code> and <code>a</code>. It is this wrapper
that performs attribute lookup in <code>c</code> when an attribute
cannot be found in <code>a</code>.</p>
<p> Aquisition wrappers provide access to the wrapped objects
through the attributes <code>aq_parent</code>, <code>aq_self</code>, <code>aq_base</code>.
In the example above, the expressions:
<pre>
'c.a.aq_parent is c'
</pre>
</p>
<p> and:
<pre>
'c.a.aq_self is a'
</pre>
</p>
<p> both evaluate to true, but the expression:
<pre>
'c.a is a'
</pre>
</p>
<p> evaluates to false, because the expression <code>c.a</code> evaluates
to an acquisition wrapper around <code>c</code> and <code>a</code>, not <code>a</code> itself.</p>
<p> The attribute <code>aq_base</code> is similar to <code>aq_self</code>. Wrappers may be
nested and <code>aq_self</code> may be a wrapped object. The <code>aq_base</code>
attribute is the underlying object with all wrappers removed.</p>
<h2> Acquisition Control</h2>
<p> Two styles of acquisition are supported in the current
ExtensionClass release, implicit and explicit aquisition.</p>
<h3> Implicit acquisition</h3>
<p> Implicit acquisition is so named because it searches for
attributes from the environment automatically whenever an
attribute cannot be obtained directly from an object or
through inheritence.</p>
<p> An attribute may be implicitly acquired if it's name does
not begin with an underscore, <code>_</code>.</p>
<p> To support implicit acquisition, an object should inherit
from the mix-in class <code>Acquisition.Implicit</code>.</p>
<h3> Explicit Acquisition</h3>
<p> When explicit acquisition is used, attributes are not
automatically obtained from the environment. Instead, the
method <code>aq_aquire</code> must be used, as in:
<pre>
print c.a.aq_acquire('color')
</pre>
</p>
<p> To support explicit acquisition, an object should inherit
from the mix-in class <code>Acquisition.Explicit</code>.</p>
<h3> Controlled Acquisition</h3>
<p> A class (or instance) can provide attribute by attribute control
over acquisition. This is done by:</p>
<ul>
<li>subclassing from <code>Acquisition.Explicit</code>, and</li>
<li>setting all attributes that should be acquired to the special
value: <code>Acquisition.Acquired</code>. Setting an attribute to this
value also allows inherited attributes to be overridden with
acquired ones.<p> For example, in:
<pre>
class C(Acquisition.Explicit):
id=1
secret=2
color=Acquisition.Acquired
__roles__=Acquisition.Acquired
</pre>
</p>
<p> The <em>only</em> attributes that are automatically acquired from
containing objects are <code>color</code>, and <code>__roles__</code>. Note also
that the <code>__roles__</code> attribute is acquired even though it's
name begins with an underscore. In fact, the special
<code>Acquisition.Acquired</code> value can be used in
<code>Acquisition.Implicit</code> objects to implicitly acquire selected
objects that smell like private objects.</p>
</li>
</ul>
<h3> Filtered Acquisition</h3>
<p> The acquisition method, <code>aq_acquire</code>, accepts two optional
arguments. The first of the additional arguments is a
"filtering" function that is used when considering whether to
acquire an object. The second of the additional arguments is an
object that is passed as extra data when calling the filtering
function and which defaults to <code>None</code>.</p>
<p> The filter function is called with five arguments:</p>
<ul>
<li>The object that the <code>aq_acquire</code> method was called on,</li>
<li>The object where an object was found,</li>
<li>The name of the object, as passed to <code>aq_acquire</code>,</li>
<li>The object found, and</li>
<li>The extra data passed to <code>aq_acquire</code>.</li>
</ul>
<p> If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.</p>
<p> For example, in:
<pre>
from Acquisition import Explicit
class HandyForTesting:
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
class E(Explicit, HandyForTesting): pass
class Nice(HandyForTesting):
isNice=1
def __str__(self):
return HandyForTesting.__str__(self)+' and I am nice!'
__repr__=__str__
a=E('a')
a.b=E('b')
a.b.c=E('c')
a.p=Nice('spam')
a.b.p=E('p')
def find_nice(self, ancestor, name, object, extra):
return hasattr(object,'isNice') and object.isNice
print a.b.c.aq_acquire('p', find_nice)
</pre>
</p>
<p> The filtered acquisition in the last line skips over the first
attribute it finds with the name <code>p</code>, because the attribute
doesn't satisfy the condition given in the filter. The output of
the last line is:
<pre>
spam(Nice) and I am nice!
</pre>
</p>
<h2> Acquisition and methods</h2>
<p> Python methods of objects that support acquisition can use
acquired attributes as in the <code>report</code> method of the first example
above. When a Python method is called on an object that is
wrapped by an acquisition wrapper, the wrapper is passed to the
method as the first argument. This rule also applies to
user-defined method types and to C methods defined in pure mix-in
classes.</p>
<p> Unfortunately, C methods defined in extension base classes that
define their own data structures, cannot use aquired attributes at
this time. This is because wrapper objects do not conform to the
data structures expected by these methods.</p>
<h2> Acquiring Acquiring objects</h2>
<p> Consider the following example:
<pre>
from Acquisition import Implicit
class C(Implicit):
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
a=C("a")
a.b=C("b")
a.b.pref="spam"
a.b.c=C("c")
a.b.c.color="red"
a.b.c.pref="eggs"
a.x=C("x")
o=a.b.c.x
</pre>
</p>
<p> The expression <code>o.color</code> might be expected to return <code>"red"</code>. In
earlier versions of ExtensionClass, however, this expression
failed. Acquired acquiring objects did not acquire from the
environment they were accessed in, because objects were only
wrapped when they were first found, and were not rewrapped as they
were passed down the acquisition tree.</p>
<p> In the current release of ExtensionClass, the expression "o.color"
does indeed return <code>"red"</code>.</p>
<p> When searching for an attribute in <code>o</code>, objects are searched in
the order <code>x</code>, <code>a</code>, <code>b</code>, <code>c</code>. So, for example, the expression,
<code>o.pref</code> returns <code>"spam"</code>, not <code>"eggs"</code>. In earlier releases of
ExtensionClass, the attempt to get the <code>pref</code> attribute from <code>o</code>
would have failed.</p>
<p> If desired, the current rules for looking up attributes in complex
expressions can best be understood through repeated application of
the <code>__of__</code> method:</p>
<dl>
<dt> <code>a.x</code></dt>
<dd><code>x.__of__(a)</code></dd>
<dt> <code>a.b</code></dt>
<dd><code>b.__of__(a)</code></dd>
<dt> <code>a.b.x</code></dt>
<dd><code>x.__of__(a).__of__(b.__of__(a))</code></dd>
<dt> <code>a.b.c</code></dt>
<dd><code>c.__of__(b.__of__(a))</code></dd>
<dt> <code>a.b.c.x</code></dt>
<dd><code>x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))</code></dd>
</dl>
<p> and by keeping in mind that attribute lookup in a wrapper
is done by trying to lookup the attribute in the wrapped object
first and then in the parent object. In the expressions above
involving the <code>__of__</code> method, lookup proceeds from left to right.</p>
<p> Note that heuristics are used to avoid most of the repeated
lookups. For example, in the expression: <code>a.b.c.x.foo</code>, the object
<code>a</code> is searched no more than once, even though it is wrapped three
times.</p>
<p><a name="ref1">[1]</a> Gil, J., Lorenz, D.,
<a href="http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz">Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism</a>,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996</p>
</body>
</html>
Acquisition
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
Acquisition [1] is a mechanism that allows objects to obtain
attributes from their environment. It is similar to inheritence,
except that, rather than traversing an inheritence hierarchy
to obtain attributes, a containment hierarchy is traversed.
The "ExtensionClass":ExtensionClass.html. release includes mix-in
extension base classes that can be used to add acquisition as a
feature to extension subclasses. These mix-in classes use the
context-wrapping feature of ExtensionClasses to implement
acquisition. Consider the following example::
import ExtensionClass, Acquisition
class C(ExtensionClass.Base):
color='red'
class A(Acquisition.Implicit):
def report(self):
print self.color
a=A()
c=C()
c.a=A()
c.a.report() # prints 'red'
d=C()
d.color='green'
d.a=a
d.a.report() # prints 'green'
a.report() # raises an attribute error
The class 'A' inherits acquisition behavior from
'Acquisition.Implicit'. The object, 'a', "has" the color of
objects 'c' and 'd' when it is accessed through them, but it
has no color by itself. The object 'a' obtains attributes
from it's environment, where it's environment is defined by
the access path used to reach 'a'.
Acquisition wrappers
When an object that supports acquisition is accessed through
an extension class instance, a special object, called an
acquisition wrapper, is returned. In the example above, the
expression 'c.a' returns an acquisition wrapper that
contains references to both 'c' and 'a'. It is this wrapper
that performs attribute lookup in 'c' when an attribute
cannot be found in 'a'.
Aquisition wrappers provide access to the wrapped objects
through the attributes 'aq_parent', 'aq_self', 'aq_base'.
In the example above, the expressions::
'c.a.aq_parent is c'
and::
'c.a.aq_self is a'
both evaluate to true, but the expression::
'c.a is a'
evaluates to false, because the expression 'c.a' evaluates
to an acquisition wrapper around 'c' and 'a', not 'a' itself.
The attribute 'aq_base' is similar to 'aq_self'. Wrappers may be
nested and 'aq_self' may be a wrapped object. The 'aq_base'
attribute is the underlying object with all wrappers removed.
Acquisition Control
Two styles of acquisition are supported in the current
ExtensionClass release, implicit and explicit aquisition.
Implicit acquisition
Implicit acquisition is so named because it searches for
attributes from the environment automatically whenever an
attribute cannot be obtained directly from an object or
through inheritence.
An attribute may be implicitly acquired if it's name does
not begin with an underscore, '_'.
To support implicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Implicit'.
Explicit Acquisition
When explicit acquisition is used, attributes are not
automatically obtained from the environment. Instead, the
method 'aq_aquire' must be used, as in::
print c.a.aq_acquire('color')
To support explicit acquisition, an object should inherit
from the mix-in class 'Acquisition.Explicit'.
Controlled Acquisition
A class (or instance) can provide attribute by attribute control
over acquisition. This is done by:
- subclassing from 'Acquisition.Explicit', and
- setting all attributes that should be acquired to the special
value: 'Acquisition.Acquired'. Setting an attribute to this
value also allows inherited attributes to be overridden with
acquired ones.
For example, in::
class C(Acquisition.Explicit):
id=1
secret=2
color=Acquisition.Acquired
__roles__=Acquisition.Acquired
The *only* attributes that are automatically acquired from
containing objects are 'color', and '__roles__'. Note also
that the '__roles__' attribute is acquired even though it's
name begins with an underscore. In fact, the special
'Acquisition.Acquired' value can be used in
'Acquisition.Implicit' objects to implicitly acquire selected
objects that smell like private objects.
Filtered Acquisition
The acquisition method, 'aq_acquire', accepts two optional
arguments. The first of the additional arguments is a
"filtering" function that is used when considering whether to
acquire an object. The second of the additional arguments is an
object that is passed as extra data when calling the filtering
function and which defaults to 'None'.
The filter function is called with five arguments:
- The object that the 'aq_acquire' method was called on,
- The object where an object was found,
- The name of the object, as passed to 'aq_acquire',
- The object found, and
- The extra data passed to 'aq_acquire'.
If the filter returns a true object that the object found is
returned, otherwise, the acquisition search continues.
For example, in::
from Acquisition import Explicit
class HandyForTesting:
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
class E(Explicit, HandyForTesting): pass
class Nice(HandyForTesting):
isNice=1
def __str__(self):
return HandyForTesting.__str__(self)+' and I am nice!'
__repr__=__str__
a=E('a')
a.b=E('b')
a.b.c=E('c')
a.p=Nice('spam')
a.b.p=E('p')
def find_nice(self, ancestor, name, object, extra):
return hasattr(object,'isNice') and object.isNice
print a.b.c.aq_acquire('p', find_nice)
The filtered acquisition in the last line skips over the first
attribute it finds with the name 'p', because the attribute
doesn't satisfy the condition given in the filter. The output of
the last line is::
spam(Nice) and I am nice!
Acquisition and methods
Python methods of objects that support acquisition can use
acquired attributes as in the 'report' method of the first example
above. When a Python method is called on an object that is
wrapped by an acquisition wrapper, the wrapper is passed to the
method as the first argument. This rule also applies to
user-defined method types and to C methods defined in pure mix-in
classes.
Unfortunately, C methods defined in extension base classes that
define their own data structures, cannot use aquired attributes at
this time. This is because wrapper objects do not conform to the
data structures expected by these methods.
Acquiring Acquiring objects
Consider the following example::
from Acquisition import Implicit
class C(Implicit):
def __init__(self, name): self.name=name
def __str__(self):
return "%s(%s)" % (self.name, self.__class__.__name__)
__repr__=__str__
a=C("a")
a.b=C("b")
a.b.pref="spam"
a.b.c=C("c")
a.b.c.color="red"
a.b.c.pref="eggs"
a.x=C("x")
o=a.b.c.x
The expression 'o.color' might be expected to return '"red"'. In
earlier versions of ExtensionClass, however, this expression
failed. Acquired acquiring objects did not acquire from the
environment they were accessed in, because objects were only
wrapped when they were first found, and were not rewrapped as they
were passed down the acquisition tree.
In the current release of ExtensionClass, the expression "o.color"
does indeed return '"red"'.
When searching for an attribute in 'o', objects are searched in
the order 'x', 'a', 'b', 'c'. So, for example, the expression,
'o.pref' returns '"spam"', not '"eggs"'. In earlier releases of
ExtensionClass, the attempt to get the 'pref' attribute from 'o'
would have failed.
If desired, the current rules for looking up attributes in complex
expressions can best be understood through repeated application of
the '__of__' method:
'a.x' -- 'x.__of__(a)'
'a.b' -- 'b.__of__(a)'
'a.b.x' -- 'x.__of__(a).__of__(b.__of__(a))'
'a.b.c' -- 'c.__of__(b.__of__(a))'
'a.b.c.x' --
'x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))'
and by keeping in mind that attribute lookup in a wrapper
is done by trying to lookup the attribute in the wrapped object
first and then in the parent object. In the expressions above
involving the '__of__' method, lookup proceeds from left to right.
Note that heuristics are used to avoid most of the repeated
lookups. For example, in the expression: 'a.b.c.x.foo', the object
'a' is searched no more than once, even though it is wrapped three
times.
.. [1] Gil, J., Lorenz, D.,
"Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz,
OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
<html>
<head>
<title>Extension Classes, Python Extension Types Become Classes</title>
</head>
<body>
<h1>Extension Classes, Python Extension Types Become Classes</h1>
<p> Jim Fulton, Digital Creations, Inc.
jim@digicool.com</p>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<h2> Abstract</h2>
<p> A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:</p>
<ul>
<li>They can be sub-classed in python,</li>
<li>They provide access to method documentation strings, and</li>
<li>They can be used to directly create new instances.</li>
</ul>
<p> An example class shows how extension classes are implemented and how
they differ from extension types.</p>
<p> Extension classes provide additional extensions to class and
instance semantics, including:</p>
<ul>
<li>A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and <a href="Acquisition.html">environmental acquisition</a>.</li>
<li>A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.</li>
<li>A protocol for class initialization that supports execution of a
special <code>__class_init__</code> method after a class has been
initialized. </li>
</ul>
<p> Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models. </p>
<h2> Releases</h2>
<p> To find out what's changed in this release,
see the <a href="release.html">release notes</a>.</p>
<h2> Problem</h2>
<p> Currently, Python provides two ways of defining new kinds of objects:</p>
<ul>
<li>Python classes</li>
<li>Extension types</li>
</ul>
<p> Each approach has it's strengths. Extension types provide much greater
control to the programmer and, generally, better performance. Because
extension types are written in C, the programmer has greater access to
external resources. (Note that Python's use of the term type has
little to do with the notion of type as a formal specification.)</p>
<p> Classes provide a higher level of abstraction and are generally much
easier to develop. Classes provide full inheritance support, while
support for inheritance when developing extension types is very
limited. Classes provide run-time meta-data, such as method documentation
strings, that are useful for documentation and discovery. Classes
act as factories for creating instances, while separate functions
must be provided to create instances of types.</p>
<p> It would be useful to combine the features of the two approaches. It
would be useful to be able to have better support for inheritance for
types, or to be able to subclass from types in Python. It would be
useful to be able to have class-like meta-data support for types and
the ability to construct instances directly from types.</p>
<p> Our software is developed in Python. When necessary, we convert
debugged Python routines and classes to C for improved
performance. In most cases, a small number of methods in a class
is responsible for most of the computation. It should be possible
to convert only these methods to C, while leaving the other method
in Python. A natural way to approach this is to create a base
class in C that contains only the performance-critical aspects of
a class' implementation and mix this base class into a Python
class. </p>
<p> We have need, in a number of projects, for semantics that are
slightly different than the usual class and instance semantics,
yet we don't want to do most of our development in C. For
example, we have developed a persistence mechanism <a href="#ref1">[1]</a> that
redefines <code>__getattr__</code> and <code>__setattr__</code> to take storage-related
actions when object state is accessed or modified. We want to be
able to take certain actions on <em>every</em> attribute reference, but
for python class instances, <code>__getattr__</code> is only called when
attribute lookup fails by normal means.</p>
<p> As another example, we would like to have greater control over how
methods are bound. Currently, when accessing a class
instance attribute, the attribute value is bound together with the
instance in a method object <em>if and only if</em> the attribute value is a
python function. For some applications, we might also want to be
able to bind extension functions, or other types of callable
objects, such as HTML document templates <a href="#ref2">[2]</a>. Furthermore,
we might want to have greater control over how objects are bound.
For example, we might want to bind instances and callable objects
with special method objects that assure that no more than one thread
accesses the object or method at one time.</p>
<p> We can provide these special semantics in extension types, but we
wish to provide them for classes developed in Python.</p>
<h2> Background</h2>
<p> At the first Python Workshop, Don Beaudry presented work <a href="#ref3">[3]</a> done
at V.I. Corp to integrate Python with C++ frameworks. This system
provided a number of important features, including:</p>
<ul>
<li>Definition of extension types that provide class-like meta-data
and that can be called to create instances.</li>
<li>Ability to subclass in python from C types.</li>
<li>Ability to define classes in python who's data are stored as
C structures rather than in dictionaries to better interface to
C and C++ libraries, and for better performance.</li>
<li>Less dynamic data structures. In particular, the data structure
for a class is declared during class definition.</li>
<li>Support for enumeration types.</li>
</ul>
<p> This work was not released, initially.</p>
<p> Shortly after the workshop, changes were made to Python to support
the sub-classing features described in <a href="#ref3">[3]</a>. These changes were not
documented until the fourth Python Workshop <a href="#ref4">[4]</a>.</p>
<p> At the third Python workshop, I presented some work I had done on
generating module documentation for extension types. Based on the
discussion at this workshop, I developed a meta-type proposal <a href="#ref5">[5]</a>.
This meta-type proposal was for an object that simply stored
meta-information for a type, for the purpose of generating module
documentation.</p>
<p> In the summer of 1996, Don Beaudry released the system described in
<a href="#ref3">[3]</a> under the name MESS <a href="#ref6">[6]</a>. MESS addresses a number of needs but
has a few drawbacks:</p>
<ul>
<li>Only single inheritance is supported.</li>
<li>The mechanisms for defining MESS extension types is very different
from and more complicated than the standard Python type creation
mechanism.</li>
<li>Defining MESS types requires the use of an extensive C
applications programming interface. This presents problems for
configuring dynamically-loaded extension modules unless the MESS
library is linked into the Python interpreter.</li>
<li>Because the system tries to do a number of different things, it is
fairly large, about 15,000 lines.</li>
<li>There is very little documentation, especially for the C
programming interface.</li>
<li>The system is a work in progress, with a number of outstanding
bugs.</li>
</ul>
<p> As MESS matures, we expect most of these problems to be addressed.</p>
<h2> Extension Classes</h2>
<p> To meet short term needs for a C-based persistence mechanism <a href="#ref1">[1]</a>, an
extension class module was developed using the mechanism described
in <a href="#ref4">[4]</a> and building on ideas from MESS <a href="#ref6">[6]</a>. The extension class module
recasts extension types as "extension classes" by seeking to
eliminate, or at least reduce semantic differences between types and
classes. The module was designed to meet the following goal:</p>
<ul>
<li>Provide class-like behavior for extension types, including
interfaces for meta information and for constructing instances.</li>
<li>Support sub-classing in Python from extension classes, with support
for multiple inheritance.</li>
<li>Provide a small hardened implementation that can be used for
current products.</li>
<li>Provide a mechanism that requires minimal modification to existing
extension types.</li>
<li>Provide a basis for research on alternative semantics for classes
and inheritance.</li>
</ul>
<p> <strong>Note:</strong> I use <em>non-standard</em> terminology here. By standard
<em>python</em> terminology, only standard python classes can be called
classes. ExtensionClass "classes" are technically just "types"
that happen to swim, walk and quack like python classes.</p>
<h3> Base extension classes and extension subclasses</h3>
<p> Base extension classes are implemented in C. Extension subclasses
are implemented in Python and inherit, directly or indirectly from
one or more base extension classes. An extension subclass may
inherit from base extension classes, extension subclasses, and
ordinary python classes. The usual inheritance order rules
apply. Currently, extension subclasses must conform to the
following two rules:</p>
<ul>
<li>The first super class listed in the class statement defining an
extension subclass must be either a base extension class or an
extension subclass. This restriction will be removed in
Python-1.5.</li>
<li>At most one base extension direct or indirect super class may
define C data members. If an extension subclass inherits from
multiple base extension classes, then all but one must be mix-in
classes that provide extension methods but no data.</li>
</ul>
<h3> Meta Information</h3>
<p> Like standard python classes, extension classes have the following
attributes containing meta-data:</p>
<dl>
<dt> <code>__doc__</code></dt>
<dd>a documentation string for the class,</dd>
<dt> <code>__name__</code></dt>
<dd>the class name,</dd>
<dt> <code>__bases__</code></dt>
<dd>a sequence of base classes,</dd>
<dt> <code>__dict__</code></dt>
<dd>a class dictionary, and</dd>
<dt> <code>__module__</code></dt>
<dd>the name of the module in which the class was
defined. </dd>
</dl>
<p> The class dictionary provides access to unbound methods and their
documentation strings, including extension methods and special
methods, such as methods that implement sequence and numeric
protocols. Unbound methods can be called with instance first
arguments.</p>
<h3> Subclass instance data</h3>
<p> Extension subclass instances have instance dictionaries, just
like Python class instances do. When fetching attribute values,
extension class instances will first try to obtain data from the
base extension class data structure, then from the instance
dictionary, then from the class dictionary, and finally from base
classes. When setting attributes, extension classes first attempt
to use extension base class attribute setting operations, and if
these fail, then data are placed in the instance dictionary.</p>
<h2> Implementing base extension classes</h2>
<p> A base extension class is implemented in much the same way that an
extension type is implemented, except:</p>
<ul>
<li>The include file, <code>ExtensionClass.h</code>, must be included.</li>
<li>The type structure is declared to be of type <code>PyExtensionClass</code>, rather
than of type <code>PyTypeObject</code>.</li>
<li>The type structure has an additional member that must be defined
after the documentation string. This extra member is a method chain
(<code>PyMethodChain</code>) containing a linked list of method definition
(<code>PyMethodDef</code>) lists. Method chains can be used to implement
method inheritance in C. Most extensions don't use method chains,
but simply define method lists, which are null-terminated arrays
of method definitions. A macro, <code>METHOD_CHAIN</code> is defined in
<code>ExtensionClass.h</code> that converts a method list to a method chain.
(See the example below.)</li>
<li>Module functions that create new instances must be replaced by
<code>__init__</code> methods that initialize, but does not create storage for
instances.</li>
<li>The extension class must be initialized and exported to the module
with:
<pre>
PyExtensionClass_Export(d,"name",type);
</pre>
</li>
</ul>
<p> where <code>name</code> is the module name and <code>type</code> is the extension class
type object.<h2> Attribute lookup</h2>
<p> Attribute lookup is performed by calling the base extension class
<code>getattr</code> operation for the base extension class that includes C
data, or for the first base extension class, if none of the base
extension classes include C data. <code>ExtensionClass.h</code> defines a
macro <code>Py_FindAttrString</code> that can be used to find an object's
attributes that are stored in the object's instance dictionary or
in the object's class or base classes:
<pre>
v = Py_FindAttrString(self,name);
</pre>
</p>
<p> where <code>name</code> is a C string containing the attribute name.</p>
<p> In addition, a macro is provided that replaces <code>Py_FindMethod</code>
calls with logic to perform the same sort of lookup that is
provided by <code>Py_FindAttrString</code>.</p>
<p> If an attribute name is contained in a Python string object,
rather than a C string object, then the macro <code>Py_FindAttr</code> should
be used to look up an attribute value.</p>
<h2> Linking</h2>
<p> The extension class mechanism was designed to be useful with
dynamically linked extension modules. Modules that implement
extension classes do not have to be linked against an extension
class library. The macro <code>PyExtensionClass_Export</code> imports the
<code>ExtensionClass</code> module and uses objects imported from this module
to initialize an extension class with necessary behavior.</p>
<h2> Example: MultiMapping objects</h2>
<p> An <a href="MultiMapping.html">example</a>, is provided that illustrates the
changes needed to convert an existing type to an ExtensionClass.</p>
</p>
<h2> Implementing base extension class constructors</h2>
<p> Some care should be taken when implementing or overriding base
class constructors. When a Python class overrides a base class
constructor and fails to call the base class constructor, a
program using the class may fail, but it will not crash the
interpreter. On the other hand, an extension subclass that
overrides a constructor in an extension base class must call the
extension base class constructor or risk crashing the interpreter.
This is because the base class constructor may set C pointers that,
if not set properly, will cause the interpreter to crash when
accessed. This is the case with the <code>MultiMapping</code> extension base
class shown in the example above.</p>
<p> If no base class constructor is provided, extension class instance
memory will be initialized to 0. It is a good idea to design
extension base classes so that instance methods check for
uninitialized memory and perform initialialization if necessary.
This was not done above to simplify the example.</p>
<h2> Overriding methods inherited from Python base classes</h2>
<p> A problem occurs when trying to overide methods inherited from
Python base classes. Consider the following example:
<pre>
from ExtensionClass import Base
class Spam:
def __init__(self, name):
self.name=name
class ECSpam(Base, Spam):
def __init__(self, name, favorite_color):
Spam.__init__(self,name)
self.favorite_color=favorite_color
</pre>
</p>
<p> This implementation will fail when an <code>ECSpam</code> object is
instantiated. The problem is that <code>ECSpam.__init__</code> calls
<code>Spam.__init__</code>, and <code>Spam.__init__</code> can only be called with a
Python instance (an object of type <code>"instance"</code>) as the first
argument. The first argument passed to <code>Spam.__init__</code> will be an
<code>ECSpam</code> instance (an object of type <code>ECSPam</code>).</p>
<p> To overcome this problem, extension classes provide a class method
<code>inheritedAttribute</code> that can be used to obtain an inherited
attribute that is suitable for calling with an extension class
instance. Using the <code>inheritedAttribute</code> method, the above
example can be rewritten as:
<pre>
from ExtensionClass import Base
class Spam:
def __init__(self, name):
self.name=name
class ECSpam(Base, Spam):
def __init__(self, name, favorite_color):
ECSpam.inheritedAttribute('__init__')(self,name)
self.favorite_color=favorite_color
</pre>
</p>
<p> This isn't as pretty but does provide the desired result.</p>
<h2> New class and instance semantics</h2>
<h3> Context Wrapping</h3>
<p> It is sometimes useful to be able to wrap up an object together
with a containing object. I call this "context wrapping"
because an object is accessed in the context of the object it is
accessed through.</p>
<h4> We have found many applications for this, including:</h4>
<ul>
<li>User-defined method objects, </li>
<li><a href="Acquisition.html">Acquisition</a>, and</li>
<li>Computed attributes</li>
</ul>
<h4> User-defined method objects</h4>
<p> Python classes wrap Python function attributes into methods. When a
class has a function attribute that is accessed as an instance
attribute, a method object is created and returned that contains
references to the original function and instance. When the method
is called, the original function is called with the instance as the
first argument followed by any arguments passed to the method.</p>
<p> Extension classes provide a similar mechanism for attributes that
are Python functions or inherited extension functions. In
addition, if an extension class attribute is an instance of an
extension class that defines an <code>__of__</code> method, then when the
attribute is accessed through an instance, it's <code>__of__</code> method
will be called to create a bound method.</p>
<p> Consider the following example:
<pre>
import ExtensionClass
class CustomMethod(ExtensionClass.Base):
def __call__(self,ob):
print 'a %s was called' % ob.__class__.__name__
class wrapper:
def __init__(self,m,o): self.meth, self.ob=m,o
def __call__(self): self.meth(self.ob)
def __of__(self,o): return self.wrapper(self,o)
class bar(ExtensionClass.Base):
hi=CustomMethod()
x=bar()
hi=x.hi()
</pre>
</p>
<p> Note that <code>ExtensionClass.Base</code> is a base extension class that
provides very basic ExtensionClass behavior. </p>
<p> When run, this program outputs: <code>a bar was called</code>.</p>
<h4> Computed Attributes</h4>
<p> It is not uncommon to wish to expose information via the
attribute interface without affecting implementation data
structures. One can use a custom <code>__getattr__</code> method to
implement computed attributes, however, this can be a bit
cumbersome and can interfere with other uses of <code>__getattr__</code>,
such as for persistence.</p>
<p> The <code>__of__</code> protocol provides a convenient way to implement
computed attributes. First, we define a ComputedAttribute
class. a ComputedAttribute is constructed with a function to
be used to compute an attribute, and calls the function when
it's <code>__of__</code> method is called:<p> import ExtensionClass</p>
<h5> class ComputedAttribute(ExtensionClass.Base):</h5>
<p> def __init__(self, func): self.func=func</p>
<p> def __of__(self, parent): return self.func(parent)</p>
</p>
<p> Then we can use this class to create computed attributes. In the
example below, we create a computed attribute, 'radius':<p> from math import sqrt</p>
<h5> class Point(ExtensionClass.Base):</h5>
<p> def __init__(self, x, y): self.x, self.y = x, y</p>
<p> radius=ComputedAttribute(lambda self: sqrt(self.x<strong>2+self.y</strong>2))</p>
</p>
<h5> which we can use just like an ordinary attribute:</h5>
<p> p=Point(2,2)
print p.radius</p>
<h3> Overriding method calls</h3>
<p> Normally, when a method is called, the function wrapped by the
method is called directly by the method. In some cases, it is
useful for user-defined logic to participate in the actual
function call. Extension classes introduce a new protocol that
provides extension classes greater control over how their
methods are called. If an extension class defines a special
method, <code>__call_method__</code>, then this method will be called to
call the functions (or other callable object) wrapped by the
method. The method. <code>__call_method__</code> should provide the same
interface as provided by the Python builtin <code>apply</code> function.</p>
<p> For example, consider the expression: <code>x.meth(arg1, arg2)</code>. The
expression is evaluated by first computing a method object that
wraps <code>x</code> and the attribute of <code>x</code> stored under the name <code>meth</code>.
Assuming that <code>x</code> has a <code>__call_method__</code> method defined, then
the <code>__call_method__</code> method of <code>x</code> will be called with two
arguments, the attribute of <code>x</code> stored under the name <code>meth</code>,
and a tuple containing <code>x</code>, <code>arg1</code>, and <code>arg2</code>.</p>
<p> To see how this feature may be used, see the Python module,
<code>Syn.py</code>, which is included in the ExtensionClass distribution.
This module provides a mix-in class that provides Java-like
"synchonized" classes that limit access to their methods to one
thread at a time.</p>
<p> An interesting application of this mechanism would be to
implement interface checking on method calls.</p>
<h3> Method attributes</h3>
<p> Methods of ExtensionClass instances can have user-defined
attributes, which are stored in their associated instances.</p>
<p> For example:
<pre>
class C(ExtensionClass.Base):
def get_secret(self):
"Get a secret"
....
c=C()
c.f.__roles__=['Trusted People']
print c.f.__roles__ # outputs ['Trusted People']
print c.f__roles__ # outputs ['Trusted People']
print C.f.__roles__ # fails, unbound method
</pre>
</p>
<p> A bound method attribute is set by setting an attribute in it's
instance with a name consisting of the concatination of the
method's <code>__name__</code> attribute and the attribute name.
Attributes cannot be set on unbound methods.</p>
<h3> Class initialization</h3>
<p> Normal Python class initialization is similar to but subtley
different from instance initialization. An instance <code>__init__</code>
function is called on an instance immediately <em>after</em> it is
created. An instance <code>__init__</code> function can use instance
information, like it's class and can pass the instance to other
functions. On the other hand, the code in class statements is
executed immediately <em>before</em> the class is created. This means
that the code in a class statement cannot use class attributes,
like <code>__bases__</code>, or pass the class to functions.</p>
<p> Extension classes provide a mechanism for specifying code to be
run <em>after</em> a class has been created. If a class or one of it's
base classes defines a <code>__class_init__</code> method, then this method
will be called just after a class has been created. The one
argument passed to the method will be the class, <em>not</em> an
instance of the class.</p>
<h2> Useful macros defined in ExtensionClass.h</h2>
<p> A number of useful macros are defined in ExtensionClass.h.
These are documented in <code>ExtensionClass.h</code>.</p>
<h2> Pickleability</h2>
<p> Classes created with ExtensionClass, including extension base
classes are automatically pickleable. The usual gymnastics
necessary to pickle <code>non-standard</code> types are not necessray for
types that have been modified to be extension base classes.</p>
<h2> Status</h2>
<p> The current release of the extension class module is <a href="http://www.digicool.com/releases/ExtensionClass/ExtensionClass-1.1.tar.gz">1.1</a>.
The core implementation has less than four thousand lines of code,
including comments. This release requires Python 1.4 or higher.</p>
<p> To find out what's changed in this release, see the
<a href="release.html">release notes</a>.</p>
<p> <a href="Installation.html">Installation instructions</a>, are provided.</p>
<h2> Issues</h2>
<p> There are a number of issues that came up in the course of this work
and that deserve mention.</p>
<ul>
<li>In Python 1.4, the class extension mechanism described in <a href="#ref4">[4]</a> required
that the first superclass in a list of super-classes must be of the
extended class type. This may not be convenient if mix-in
behavior is desired. If a list of base classes starts with a
standard python class, but includes an extension class, then an
error was raised. It would be more useful if, when a list of base
classes contains one or more objects that are not python classes,
the first such object was used to control the extended class
definition. To get around this, the <code>ExtensionClass</code> module exports
a base extension class, <code>Base</code>, that can be used as the first base
class in a list of base classes to assure that an extension
subclass is created.<p> Python 1.5 allows the class extension even if the first non-class
object in the list of base classes is not the first object in
the list. This issue appears to go away in Python 1.5, however,
the restriction that the first non-class object in a list of
base classes must be the first in the list may reappear in later
versions of Python.</p>
</li>
<li>Currently, only one base extension class can define any data in
C. The data layout of subclasses-instances is the same as for the
base class that defines data in C, except that the data structure
is extended to hold an instance dictionary. The data structure
begins with a standard python header, and extension methods expect
the C instance data to occur immediately after the object header. If
two or more base classes defined C data, the methods for the
different base classes would expect their data to be in the same
location. A solution might be to allocate base class instances and
store pointers to these instances in the subclass data structure.
The method binding mechanism would have to be a more complicated
to make sure that methods were bound to the correct base data
structure. Alternatively, the signature of C methods could be
expanded to allow pointers to expected class data to be passed
in addition to object pointers.</li>
<li>There is currently no support for sub-classing in C, beyond that
provided by method chains.</li>
<li>Rules for mixed-type arithmetic are different for python class
instances than they are for extension type instances. Python
classes can define right and left versions of numeric binary
operators, or they can define a coercion operator for converting
binary operator operands to a common type. For extension types,
only the latter, coercion-based, approach is supported. The
coercion-based approach does not work well for many data types for
which coercion rules depend on the operator. Because extension
classes are based on extension types, they are currently limited
to the coercion-based approach. It should be possible to
extend the extension class implementation to allow both types of
mixed-type arithmetic control.</li>
<li>I considered making extension classes immutable, meaning that
class attributes could not be set after class creation. I also
considered making extension subclasses cache inherited
attributes. Both of these are related and attractive for some
applications, however, I decided that it would be better to retain
standard class instance semantics and provide these features as
options at a later time.</li>
<li>The extension class module defines new method types to bind C and
python methods to extension class instances. It would be useful
for these method objects to provide access to function call
information, such as the number and names of arguments and the
number of defaults, by parsing extension function documentation
strings.</li>
</ul>
<h2> Applications</h2>
<p> Aside from test and demonstration applications, the extension class
mechanism has been used to provide an extension-based implementation
of the persistence mechanism described in <a href="#ref1">[1]</a>. We have developed
this further to provide features such as automatic deactivation of
objects not used after some period of time and to provide more
efficient persistent-object cache management.</p>
<p> Acquisition has been heavily used in our recent products.
Synchonized classes have also been used in recent products.</p>
<h2> Summary</h2>
<p> The extension-class mechanism described here provides a way to add
class services to extension types. It allows:
<ul>
<li>Sub-classing extension classes in Python,</li>
<li>Construction of extension class instances by calling extension
classes,</li>
<li>Extension classes to provide meta-data, such as unbound methods
and their documentation string.</li>
</ul>
</p>
<p> In addition, the extension class module provides a relatively
concise example of the use of mechanisms that were added to Python
to support MESS <a href="#ref6">[6]</a>, and that were described at the fourth Python
Workshop <a href="#ref4">[4]</a>. It is hoped that this will spur research in improved
and specialized models for class implementation in Python.</p>
<p> References</p>
<p><a name="ref1">[1]</a> Fulton, J., <a href="http://www.digicool.com/papers/Persistence.html">Providing Persistence for World-Wide-Web Applications</a>,
Proceedings of the 5th Python Workshop.</p>
<p><a name="ref2">[2]</a> Page, R. and Cropper, S., <a href="http://www.digicool.com/papers/DocumentTemplate.html">Document Template</a>,
Proceedings of the 5th Python Workshop.</p>
<p><a name="ref3">[3]</a> Beaudry, D., <a href="http://www.python.org/workshops/1994-11/BuiltInClasses/BuiltInClasses_1.html">Deriving Built-In Classes in Python</a>,
Proceedings of the First International Python Workshop.</p>
<p><a name="ref4">[4]</a> Van Rossum, G., <a href="http://www.python.org/workshops/1996-06/notes/thursday.html">Don Beaudry Hack - MESS</a>,
presented in the Developer's Future Enhancements session of the
4th Python Workshop. </p>
<p><a name="ref5">[5]</a> Fulton, J., <a href="http://www.digicool.com/jim/MetaType.c">Meta-Type Object</a>,
This is a small proposal, the text of which is contained in a
sample implementation source file, </p>
<p><a name="ref6">[6]</a> Beaudry, D., and Ascher, D., <a href="http://starship.skyport.net/~da/mess/">The Meta-Extension Set</a>.</p>
</body>
</html>
Extension Classes, Python Extension Types Become Classes
Jim Fulton, Digital Creations, Inc.
jim@digicool.com
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
Abstract
A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:
- They can be sub-classed in python,
- They provide access to method documentation strings, and
- They can be used to directly create new instances.
An example class shows how extension classes are implemented and how
they differ from extension types.
Extension classes provide additional extensions to class and
instance semantics, including:
- A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and "environmental acquisition":Acquisition.html.
- A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.
- A protocol for class initialization that supports execution of a
special '__class_init__' method after a class has been
initialized.
Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models.
Releases
To find out what's changed in this release,
see the "release notes":release.html.
Problem
Currently, Python provides two ways of defining new kinds of objects:
- Python classes
- Extension types
Each approach has it's strengths. Extension types provide much greater
control to the programmer and, generally, better performance. Because
extension types are written in C, the programmer has greater access to
external resources. (Note that Python's use of the term type has
little to do with the notion of type as a formal specification.)
Classes provide a higher level of abstraction and are generally much
easier to develop. Classes provide full inheritance support, while
support for inheritance when developing extension types is very
limited. Classes provide run-time meta-data, such as method documentation
strings, that are useful for documentation and discovery. Classes
act as factories for creating instances, while separate functions
must be provided to create instances of types.
It would be useful to combine the features of the two approaches. It
would be useful to be able to have better support for inheritance for
types, or to be able to subclass from types in Python. It would be
useful to be able to have class-like meta-data support for types and
the ability to construct instances directly from types.
Our software is developed in Python. When necessary, we convert
debugged Python routines and classes to C for improved
performance. In most cases, a small number of methods in a class
is responsible for most of the computation. It should be possible
to convert only these methods to C, while leaving the other method
in Python. A natural way to approach this is to create a base
class in C that contains only the performance-critical aspects of
a class' implementation and mix this base class into a Python
class.
We have need, in a number of projects, for semantics that are
slightly different than the usual class and instance semantics,
yet we don't want to do most of our development in C. For
example, we have developed a persistence mechanism [1] that
redefines '__getattr__' and '__setattr__' to take storage-related
actions when object state is accessed or modified. We want to be
able to take certain actions on *every* attribute reference, but
for python class instances, '__getattr__' is only called when
attribute lookup fails by normal means.
As another example, we would like to have greater control over how
methods are bound. Currently, when accessing a class
instance attribute, the attribute value is bound together with the
instance in a method object *if and only if* the attribute value is a
python function. For some applications, we might also want to be
able to bind extension functions, or other types of callable
objects, such as HTML document templates [2]. Furthermore,
we might want to have greater control over how objects are bound.
For example, we might want to bind instances and callable objects
with special method objects that assure that no more than one thread
accesses the object or method at one time.
We can provide these special semantics in extension types, but we
wish to provide them for classes developed in Python.
Background
At the first Python Workshop, Don Beaudry presented work [3] done
at V.I. Corp to integrate Python with C++ frameworks. This system
provided a number of important features, including:
- Definition of extension types that provide class-like meta-data
and that can be called to create instances.
- Ability to subclass in python from C types.
- Ability to define classes in python who's data are stored as
C structures rather than in dictionaries to better interface to
C and C++ libraries, and for better performance.
- Less dynamic data structures. In particular, the data structure
for a class is declared during class definition.
- Support for enumeration types.
This work was not released, initially.
Shortly after the workshop, changes were made to Python to support
the sub-classing features described in [3]. These changes were not
documented until the fourth Python Workshop [4].
At the third Python workshop, I presented some work I had done on
generating module documentation for extension types. Based on the
discussion at this workshop, I developed a meta-type proposal [5].
This meta-type proposal was for an object that simply stored
meta-information for a type, for the purpose of generating module
documentation.
In the summer of 1996, Don Beaudry released the system described in
[3] under the name MESS [6]. MESS addresses a number of needs but
has a few drawbacks:
- Only single inheritance is supported.
- The mechanisms for defining MESS extension types is very different
from and more complicated than the standard Python type creation
mechanism.
- Defining MESS types requires the use of an extensive C
applications programming interface. This presents problems for
configuring dynamically-loaded extension modules unless the MESS
library is linked into the Python interpreter.
- Because the system tries to do a number of different things, it is
fairly large, about 15,000 lines.
- There is very little documentation, especially for the C
programming interface.
- The system is a work in progress, with a number of outstanding
bugs.
As MESS matures, we expect most of these problems to be addressed.
Extension Classes
To meet short term needs for a C-based persistence mechanism [1], an
extension class module was developed using the mechanism described
in [4] and building on ideas from MESS [6]. The extension class module
recasts extension types as "extension classes" by seeking to
eliminate, or at least reduce semantic differences between types and
classes. The module was designed to meet the following goal:
- Provide class-like behavior for extension types, including
interfaces for meta information and for constructing instances.
- Support sub-classing in Python from extension classes, with support
for multiple inheritance.
- Provide a small hardened implementation that can be used for
current products.
- Provide a mechanism that requires minimal modification to existing
extension types.
- Provide a basis for research on alternative semantics for classes
and inheritance.
**Note:** I use *non-standard* terminology here. By standard
*python* terminology, only standard python classes can be called
classes. ExtensionClass "classes" are technically just "types"
that happen to swim, walk and quack like python classes.
Base extension classes and extension subclasses
Base extension classes are implemented in C. Extension subclasses
are implemented in Python and inherit, directly or indirectly from
one or more base extension classes. An extension subclass may
inherit from base extension classes, extension subclasses, and
ordinary python classes. The usual inheritance order rules
apply. Currently, extension subclasses must conform to the
following two rules:
- The first super class listed in the class statement defining an
extension subclass must be either a base extension class or an
extension subclass. This restriction will be removed in
Python-1.5.
- At most one base extension direct or indirect super class may
define C data members. If an extension subclass inherits from
multiple base extension classes, then all but one must be mix-in
classes that provide extension methods but no data.
Meta Information
Like standard python classes, extension classes have the following
attributes containing meta-data:
'__doc__' -- a documentation string for the class,
'__name__' -- the class name,
'__bases__' -- a sequence of base classes,
'__dict__' -- a class dictionary, and
'__module__' -- the name of the module in which the class was
defined.
The class dictionary provides access to unbound methods and their
documentation strings, including extension methods and special
methods, such as methods that implement sequence and numeric
protocols. Unbound methods can be called with instance first
arguments.
Subclass instance data
Extension subclass instances have instance dictionaries, just
like Python class instances do. When fetching attribute values,
extension class instances will first try to obtain data from the
base extension class data structure, then from the instance
dictionary, then from the class dictionary, and finally from base
classes. When setting attributes, extension classes first attempt
to use extension base class attribute setting operations, and if
these fail, then data are placed in the instance dictionary.
Implementing base extension classes
A base extension class is implemented in much the same way that an
extension type is implemented, except:
- The include file, 'ExtensionClass.h', must be included.
- The type structure is declared to be of type 'PyExtensionClass', rather
than of type 'PyTypeObject'.
- The type structure has an additional member that must be defined
after the documentation string. This extra member is a method chain
('PyMethodChain') containing a linked list of method definition
('PyMethodDef') lists. Method chains can be used to implement
method inheritance in C. Most extensions don't use method chains,
but simply define method lists, which are null-terminated arrays
of method definitions. A macro, 'METHOD_CHAIN' is defined in
'ExtensionClass.h' that converts a method list to a method chain.
(See the example below.)
- Module functions that create new instances must be replaced by
'__init__' methods that initialize, but does not create storage for
instances.
- The extension class must be initialized and exported to the module
with::
PyExtensionClass_Export(d,"name",type);
where 'name' is the module name and 'type' is the extension class
type object.
Attribute lookup
Attribute lookup is performed by calling the base extension class
'getattr' operation for the base extension class that includes C
data, or for the first base extension class, if none of the base
extension classes include C data. 'ExtensionClass.h' defines a
macro 'Py_FindAttrString' that can be used to find an object's
attributes that are stored in the object's instance dictionary or
in the object's class or base classes::
v = Py_FindAttrString(self,name);
where 'name' is a C string containing the attribute name.
In addition, a macro is provided that replaces 'Py_FindMethod'
calls with logic to perform the same sort of lookup that is
provided by 'Py_FindAttrString'.
If an attribute name is contained in a Python string object,
rather than a C string object, then the macro 'Py_FindAttr' should
be used to look up an attribute value.
Linking
The extension class mechanism was designed to be useful with
dynamically linked extension modules. Modules that implement
extension classes do not have to be linked against an extension
class library. The macro 'PyExtensionClass_Export' imports the
'ExtensionClass' module and uses objects imported from this module
to initialize an extension class with necessary behavior.
Example: MultiMapping objects
An "example":MultiMapping.html, is provided that illustrates the
changes needed to convert an existing type to an ExtensionClass.
Implementing base extension class constructors
Some care should be taken when implementing or overriding base
class constructors. When a Python class overrides a base class
constructor and fails to call the base class constructor, a
program using the class may fail, but it will not crash the
interpreter. On the other hand, an extension subclass that
overrides a constructor in an extension base class must call the
extension base class constructor or risk crashing the interpreter.
This is because the base class constructor may set C pointers that,
if not set properly, will cause the interpreter to crash when
accessed. This is the case with the 'MultiMapping' extension base
class shown in the example above.
If no base class constructor is provided, extension class instance
memory will be initialized to 0. It is a good idea to design
extension base classes so that instance methods check for
uninitialized memory and perform initialialization if necessary.
This was not done above to simplify the example.
Overriding methods inherited from Python base classes
A problem occurs when trying to overide methods inherited from
Python base classes. Consider the following example::
from ExtensionClass import Base
class Spam:
def __init__(self, name):
self.name=name
class ECSpam(Base, Spam):
def __init__(self, name, favorite_color):
Spam.__init__(self,name)
self.favorite_color=favorite_color
This implementation will fail when an 'ECSpam' object is
instantiated. The problem is that 'ECSpam.__init__' calls
'Spam.__init__', and 'Spam.__init__' can only be called with a
Python instance (an object of type '"instance"') as the first
argument. The first argument passed to 'Spam.__init__' will be an
'ECSpam' instance (an object of type 'ECSPam').
To overcome this problem, extension classes provide a class method
'inheritedAttribute' that can be used to obtain an inherited
attribute that is suitable for calling with an extension class
instance. Using the 'inheritedAttribute' method, the above
example can be rewritten as::
from ExtensionClass import Base
class Spam:
def __init__(self, name):
self.name=name
class ECSpam(Base, Spam):
def __init__(self, name, favorite_color):
ECSpam.inheritedAttribute('__init__')(self,name)
self.favorite_color=favorite_color
This isn't as pretty but does provide the desired result.
New class and instance semantics
Context Wrapping
It is sometimes useful to be able to wrap up an object together
with a containing object. I call this "context wrapping"
because an object is accessed in the context of the object it is
accessed through.
We have found many applications for this, including:
- User-defined method objects,
- "Acquisition":Acquisition.html, and
- Computed attributes
User-defined method objects
Python classes wrap Python function attributes into methods. When a
class has a function attribute that is accessed as an instance
attribute, a method object is created and returned that contains
references to the original function and instance. When the method
is called, the original function is called with the instance as the
first argument followed by any arguments passed to the method.
Extension classes provide a similar mechanism for attributes that
are Python functions or inherited extension functions. In
addition, if an extension class attribute is an instance of an
extension class that defines an '__of__' method, then when the
attribute is accessed through an instance, it's '__of__' method
will be called to create a bound method.
Consider the following example::
import ExtensionClass
class CustomMethod(ExtensionClass.Base):
def __call__(self,ob):
print 'a %s was called' % ob.__class__.__name__
class wrapper:
def __init__(self,m,o): self.meth, self.ob=m,o
def __call__(self): self.meth(self.ob)
def __of__(self,o): return self.wrapper(self,o)
class bar(ExtensionClass.Base):
hi=CustomMethod()
x=bar()
hi=x.hi()
Note that 'ExtensionClass.Base' is a base extension class that
provides very basic ExtensionClass behavior.
When run, this program outputs: 'a bar was called'.
Computed Attributes
It is not uncommon to wish to expose information via the
attribute interface without affecting implementation data
structures. One can use a custom '__getattr__' method to
implement computed attributes, however, this can be a bit
cumbersome and can interfere with other uses of '__getattr__',
such as for persistence.
The '__of__' protocol provides a convenient way to implement
computed attributes. First, we define a ComputedAttribute
class. a ComputedAttribute is constructed with a function to
be used to compute an attribute, and calls the function when
it's '__of__' method is called:
import ExtensionClass
class ComputedAttribute(ExtensionClass.Base):
def __init__(self, func): self.func=func
def __of__(self, parent): return self.func(parent)
Then we can use this class to create computed attributes. In the
example below, we create a computed attribute, 'radius':
from math import sqrt
class Point(ExtensionClass.Base):
def __init__(self, x, y): self.x, self.y = x, y
radius=ComputedAttribute(lambda self: sqrt(self.x**2+self.y**2))
which we can use just like an ordinary attribute:
p=Point(2,2)
print p.radius
Overriding method calls
Normally, when a method is called, the function wrapped by the
method is called directly by the method. In some cases, it is
useful for user-defined logic to participate in the actual
function call. Extension classes introduce a new protocol that
provides extension classes greater control over how their
methods are called. If an extension class defines a special
method, '__call_method__', then this method will be called to
call the functions (or other callable object) wrapped by the
method. The method. '__call_method__' should provide the same
interface as provided by the Python builtin 'apply' function.
For example, consider the expression: 'x.meth(arg1, arg2)'. The
expression is evaluated by first computing a method object that
wraps 'x' and the attribute of 'x' stored under the name 'meth'.
Assuming that 'x' has a '__call_method__' method defined, then
the '__call_method__' method of 'x' will be called with two
arguments, the attribute of 'x' stored under the name 'meth',
and a tuple containing 'x', 'arg1', and 'arg2'.
To see how this feature may be used, see the Python module,
'Syn.py', which is included in the ExtensionClass distribution.
This module provides a mix-in class that provides Java-like
"synchonized" classes that limit access to their methods to one
thread at a time.
An interesting application of this mechanism would be to
implement interface checking on method calls.
Method attributes
Methods of ExtensionClass instances can have user-defined
attributes, which are stored in their associated instances.
For example::
class C(ExtensionClass.Base):
def get_secret(self):
"Get a secret"
....
c=C()
c.f.__roles__=['Trusted People']
print c.f.__roles__ # outputs ['Trusted People']
print c.f__roles__ # outputs ['Trusted People']
print C.f.__roles__ # fails, unbound method
A bound method attribute is set by setting an attribute in it's
instance with a name consisting of the concatination of the
method's '__name__' attribute and the attribute name.
Attributes cannot be set on unbound methods.
Class initialization
Normal Python class initialization is similar to but subtley
different from instance initialization. An instance '__init__'
function is called on an instance immediately *after* it is
created. An instance '__init__' function can use instance
information, like it's class and can pass the instance to other
functions. On the other hand, the code in class statements is
executed immediately *before* the class is created. This means
that the code in a class statement cannot use class attributes,
like '__bases__', or pass the class to functions.
Extension classes provide a mechanism for specifying code to be
run *after* a class has been created. If a class or one of it's
base classes defines a '__class_init__' method, then this method
will be called just after a class has been created. The one
argument passed to the method will be the class, *not* an
instance of the class.
Useful macros defined in ExtensionClass.h
A number of useful macros are defined in ExtensionClass.h.
These are documented in 'ExtensionClass.h'.
Pickleability
Classes created with ExtensionClass, including extension base
classes are automatically pickleable. The usual gymnastics
necessary to pickle 'non-standard' types are not necessray for
types that have been modified to be extension base classes.
Status
The current release of the extension class module is "1.1",
http://www.digicool.com/releases/ExtensionClass/ExtensionClass-1.1.tar.gz.
The core implementation has less than four thousand lines of code,
including comments. This release requires Python 1.4 or higher.
To find out what's changed in this release, see the
"release notes":release.html.
"Installation instructions":Installation.html, are provided.
Issues
There are a number of issues that came up in the course of this work
and that deserve mention.
- In Python 1.4, the class extension mechanism described in [4] required
that the first superclass in a list of super-classes must be of the
extended class type. This may not be convenient if mix-in
behavior is desired. If a list of base classes starts with a
standard python class, but includes an extension class, then an
error was raised. It would be more useful if, when a list of base
classes contains one or more objects that are not python classes,
the first such object was used to control the extended class
definition. To get around this, the 'ExtensionClass' module exports
a base extension class, 'Base', that can be used as the first base
class in a list of base classes to assure that an extension
subclass is created.
Python 1.5 allows the class extension even if the first non-class
object in the list of base classes is not the first object in
the list. This issue appears to go away in Python 1.5, however,
the restriction that the first non-class object in a list of
base classes must be the first in the list may reappear in later
versions of Python.
- Currently, only one base extension class can define any data in
C. The data layout of subclasses-instances is the same as for the
base class that defines data in C, except that the data structure
is extended to hold an instance dictionary. The data structure
begins with a standard python header, and extension methods expect
the C instance data to occur immediately after the object header. If
two or more base classes defined C data, the methods for the
different base classes would expect their data to be in the same
location. A solution might be to allocate base class instances and
store pointers to these instances in the subclass data structure.
The method binding mechanism would have to be a more complicated
to make sure that methods were bound to the correct base data
structure. Alternatively, the signature of C methods could be
expanded to allow pointers to expected class data to be passed
in addition to object pointers.
- There is currently no support for sub-classing in C, beyond that
provided by method chains.
- Rules for mixed-type arithmetic are different for python class
instances than they are for extension type instances. Python
classes can define right and left versions of numeric binary
operators, or they can define a coercion operator for converting
binary operator operands to a common type. For extension types,
only the latter, coercion-based, approach is supported. The
coercion-based approach does not work well for many data types for
which coercion rules depend on the operator. Because extension
classes are based on extension types, they are currently limited
to the coercion-based approach. It should be possible to
extend the extension class implementation to allow both types of
mixed-type arithmetic control.
- I considered making extension classes immutable, meaning that
class attributes could not be set after class creation. I also
considered making extension subclasses cache inherited
attributes. Both of these are related and attractive for some
applications, however, I decided that it would be better to retain
standard class instance semantics and provide these features as
options at a later time.
- The extension class module defines new method types to bind C and
python methods to extension class instances. It would be useful
for these method objects to provide access to function call
information, such as the number and names of arguments and the
number of defaults, by parsing extension function documentation
strings.
Applications
Aside from test and demonstration applications, the extension class
mechanism has been used to provide an extension-based implementation
of the persistence mechanism described in [1]. We have developed
this further to provide features such as automatic deactivation of
objects not used after some period of time and to provide more
efficient persistent-object cache management.
Acquisition has been heavily used in our recent products.
Synchonized classes have also been used in recent products.
Summary
The extension-class mechanism described here provides a way to add
class services to extension types. It allows:
- Sub-classing extension classes in Python,
- Construction of extension class instances by calling extension
classes,
- Extension classes to provide meta-data, such as unbound methods
and their documentation string.
In addition, the extension class module provides a relatively
concise example of the use of mechanisms that were added to Python
to support MESS [6], and that were described at the fourth Python
Workshop [4]. It is hoped that this will spur research in improved
and specialized models for class implementation in Python.
References
.. [1] Fulton, J., "Providing Persistence for World-Wide-Web Applications",
http://www.digicool.com/papers/Persistence.html,
Proceedings of the 5th Python Workshop.
.. [2] Page, R. and Cropper, S., "Document Template",
http://www.digicool.com/papers/DocumentTemplate.html,
Proceedings of the 5th Python Workshop.
.. [3] Beaudry, D., "Deriving Built-In Classes in Python",
http://www.python.org/workshops/1994-11/BuiltInClasses/BuiltInClasses_1.html,
Proceedings of the First International Python Workshop.
.. [4] Van Rossum, G., "Don Beaudry Hack - MESS",
http://www.python.org/workshops/1996-06/notes/thursday.html,
presented in the Developer's Future Enhancements session of the
4th Python Workshop.
.. [5] Fulton, J., "Meta-Type Object",
http://www.digicool.com/jim/MetaType.c,
This is a small proposal, the text of which is contained in a
sample implementation source file,
.. [6] Beaudry, D., and Ascher, D., "The Meta-Extension Set",
http://starship.skyport.net/~da/mess/.
<html>
<head>
<title>This is the InnerLinkTest</title>
</head>
<body>
<h1>This is the InnerLinkTest</h1>
<p> see also <a href="#ref1">[1]</a> and <a href="#ref2">[2]</a></p>
<p> <a name="ref1">[1]</a> "Zope Book" by Amos Lattmeier and Michel Pelletier</p>
<p> <a name="ref2">[2]</a> "Python Book" by Guido van Rossum</p>
</body>
</html>
This is the InnerLinkTest
see also [1] and [2]
.. [1] "Zope Book" by Amos Lattmeier and Michel Pelletier
.. [2] "Python Book" by Guido van Rossum
<html>
<head>
<title>This is LinkTest</title>
</head>
<body>
<h1>This is LinkTest</h1>
<ul>
<li>please click <a href="/Members/Zope">here</a></li>
<li>please click <a href="/Members/Zope?a=b&c=d%20blabla">here</a></li>
<li>please click <a href="http://www.zope.org">here</a></li>
<li>please click <a href="http://www.zope.org/members/">here</a></li>
<li>please click <a href="http://www.zope.org:2001">here</a> </li>
<li>please click <a href="http://www.zope.org:2001/members/">here</a></li>
<li>please click <a href="http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test">here</a> </li>
<li>please click <a href="http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test">here</a> </li>
<li>please click <a href="http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test">here</a> </li>
</ul>
<p> And now a paragraph with <a href="http://www.zope-rocks.org">Link 1</a> and
<a href="http://www.zope-is-kewl.com">Link 2</a> and <a href="http://www.freshmeat.net">one more link - yeah.</a></p>
</body>
</html>
This is LinkTest
- please click "here":/Members/Zope
- please click "here":/Members/Zope?a=b&c=d%20blabla
- please click "here":http://www.zope.org
- please click "here":http://www.zope.org/members/
- please click "here":http://www.zope.org:2001
- please click "here":http://www.zope.org:2001/members/
- please click "here":http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test
- please click "here":http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test
- please click "here", http://www.zope.org:2001/%20/Members/zope?a=222&b=213&_xc=just%20a%20test
And now a paragraph with "Link 1":http://www.zope-rocks.org and
"Link 2":http://www.zope-is-kewl.com and "one more link - yeah.":http://www.freshmeat.net
<html>
<head>
<title>Example: MultiMapping objects</title>
</head>
<body>
<h1>Example: MultiMapping objects</h1>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<p> As an example, consider an extension class that implements a
"MultiMapping". A multi-mapping is an object that encapsulates 0
or more mapping objects. When an attempt is made to lookup an
object, the encapsulated mapping objects are searched until an
object is found.</p>
<p> Consider an implementation of a MultiMapping extension type,
without use of the extension class mechanism:
<pre>
#include "Python.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyTypeObject MMtype;
static PyObject *
MM_push(MMobject *self, PyObject *args){
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &amp;src)) return NULL;
UNLESS(-1 != PyList_Append(self-&gt;data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(MMobject *self, PyObject *args){
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self-&gt;data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self-&gt;data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self-&gt;data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static PyObject *
newMMobject(PyObject *ignored, PyObject *args){
MMobject *self;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self = PyObject_NEW(MMobject, &amp;MMtype)) return NULL;
UNLESS(self-&gt;data=PyList_New(0)) goto err;
return (PyObject *)self;
err:
Py_DECREF(self);
return NULL;
}
static void
MM_dealloc(MMobject *self){
Py_XDECREF(self-&gt;data);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(MMobject *self, char *name){
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(MMobject *self){
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self-&gt;data))) return -1;
while(--i &gt;= 0)
{
e=PyList_GetItem(self-&gt;data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyObject *
MM_subscript(MMobject *self, PyObject *key){
long i;
PyObject *e;
UNLESS(-1 != (i=PyList_Size(self-&gt;data))) return NULL;
while(--i &gt;= 0)
{
e=PyList_GetItem(self-&gt;data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;
static PyTypeObject MMtype = {
PyObject_HEAD_INIT(&amp;PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&amp;MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
MMtype__doc__ /* Documentation string */
};
static struct PyMethodDef MultiMapping_methods[] = {
{"MultiMapping", (PyCFunction)newMMobject, 1,
"MultiMapping() -- Create a new empty multi-mapping"},
{NULL, NULL} /* sentinel */
};
void
initMultiMapping(){
PyObject *m;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}
</pre>
</p>
<p> This module defines an extension type, <code>MultiMapping</code>, and exports a
module function, <code>MultiMapping</code>, that creates <code>MultiMapping</code>
Instances. The type provides two methods, <code>push</code>, and <code>pop</code>, for
adding and removing mapping objects to the multi-mapping.
The type provides mapping behavior, implementing mapping length
and subscript operators but not mapping a subscript assignment
operator.</p>
<p> Now consider an extension class implementation of MultiMapping
objects:
<pre>
#include "Python.h"
#include "ExtensionClass.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyExtensionClass MMtype;
static PyObject *
MM_push(self, args)
MMobject *self;
PyObject *args;
{
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &amp;src)) return NULL;
UNLESS(-1 != PyList_Append(self-&gt;data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(self, args)
MMobject *self;
PyObject *args;
{
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self-&gt;data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self-&gt;data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self-&gt;data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
MM__init__(self, args)
MMobject *self;
PyObject *args;
{
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self-&gt;data=PyList_New(0)) goto err;
Py_INCREF(Py_None);
return Py_None;
err:
Py_DECREF(self);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"__init__", (PyCFunction)MM__init__, 1,
"__init__() -- Create a new empty multi-mapping"},
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static void
MM_dealloc(self)
MMobject *self;
{
Py_XDECREF(self-&gt;data);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(self, name)
MMobject *self;
char *name;
{
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(self)
MMobject *self;
{
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self-&gt;data))) return -1;
while(--i &gt;= 0)
{
e=PyList_GetItem(self-&gt;data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyObject *
MM_subscript(self, key)
MMobject *self;
PyObject *key;
{
long i;
PyObject *e;
UNLESS(-1 != (i=PyList_Size(self-&gt;data))) return NULL;
while(--i &gt;= 0)
{
e=PyList_GetItem(self-&gt;data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;
static PyExtensionClass MMtype = {
PyObject_HEAD_INIT(&amp;PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&amp;MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
MMtype__doc__, /* Documentation string */
METHOD_CHAIN(MM_methods)
};
static struct PyMethodDef MultiMapping_methods[] = {
{NULL, NULL} /* sentinel */
};
void
initMultiMapping()
{
PyObject *m, *d;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}
</pre>
</p>
<p> This version includes <code>ExtensionClass.h</code>. The two declarations of
<code>MMtype</code> have been changed from <code>PyTypeObject</code> to <code>PyExtensionClass</code>.
The <code>METHOD_CHAIN</code> macro has been used to add methods to the end of
the definition for <code>MMtype</code>. The module function, newMMobject has
been replaced by the <code>MMtype</code> method, <code>MM__init__</code>. Note that this
method does not create or return a new object. Finally, the lines:
<pre>
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
</pre>
</p>
<p> Have been added to both initialize the extension class and to export
it in the module dictionary.</p>
<p> To use this module, compile, link, and import it as with any other
extension module. The following python code illustrates the
module's use:
<pre>
from MultiMapping import MultiMapping
m=MultiMapping()
m.push({'spam':1, 'eggs':2})
m.push({'spam':3, 'ham':4})
m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error
</pre>
</p>
<p> Creating the <code>MultiMapping</code> object took three steps, one to create
an empty <code>MultiMapping</code>, and two to add mapping objects to it. We
might wish to simplify the process of creating MultiMapping
objects by providing a constructor that takes source mapping
objects as parameters. We can do this by sub-classing MultiMapping
in Python:
<pre>
from MultiMapping import MultiMapping
class ExtendedMultiMapping(MultiMapping):
def __init__(self,*data):
MultiMapping.__init__(self)
for d in data: self.push(d)
m=ExtendedMultiMapping({'spam':1, 'eggs':2}, {'spam':3, 'ham':4})
m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error
</pre>
</p>
<p> Note that the source file included in the ExtensionClass
distribution has numerous enhancements beyond the version shown in
this document.</p>
</body>
</html>
Example: MultiMapping objects
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
As an example, consider an extension class that implements a
"MultiMapping". A multi-mapping is an object that encapsulates 0
or more mapping objects. When an attempt is made to lookup an
object, the encapsulated mapping objects are searched until an
object is found.
Consider an implementation of a MultiMapping extension type,
without use of the extension class mechanism::
#include "Python.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyTypeObject MMtype;
static PyObject *
MM_push(MMobject *self, PyObject *args){
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(MMobject *self, PyObject *args){
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static PyObject *
newMMobject(PyObject *ignored, PyObject *args){
MMobject *self;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self = PyObject_NEW(MMobject, &MMtype)) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
return (PyObject *)self;
err:
Py_DECREF(self);
return NULL;
}
static void
MM_dealloc(MMobject *self){
Py_XDECREF(self->data);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(MMobject *self, char *name){
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(MMobject *self){
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyObject *
MM_subscript(MMobject *self, PyObject *key){
long i;
PyObject *e;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;
static PyTypeObject MMtype = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
MMtype__doc__ /* Documentation string */
};
static struct PyMethodDef MultiMapping_methods[] = {
{"MultiMapping", (PyCFunction)newMMobject, 1,
"MultiMapping() -- Create a new empty multi-mapping"},
{NULL, NULL} /* sentinel */
};
void
initMultiMapping(){
PyObject *m;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}
This module defines an extension type, 'MultiMapping', and exports a
module function, 'MultiMapping', that creates 'MultiMapping'
Instances. The type provides two methods, 'push', and 'pop', for
adding and removing mapping objects to the multi-mapping.
The type provides mapping behavior, implementing mapping length
and subscript operators but not mapping a subscript assignment
operator.
Now consider an extension class implementation of MultiMapping
objects::
#include "Python.h"
#include "ExtensionClass.h"
#define UNLESS(E) if(!(E))
typedef struct {
PyObject_HEAD
PyObject *data;
} MMobject;
staticforward PyExtensionClass MMtype;
static PyObject *
MM_push(self, args)
MMobject *self;
PyObject *args;
{
PyObject *src;
UNLESS(PyArg_ParseTuple(args, "O", &src)) return NULL;
UNLESS(-1 != PyList_Append(self->data,src)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
MM_pop(self, args)
MMobject *self;
PyObject *args;
{
long l;
PyObject *r;
static PyObject *emptyList=0;
UNLESS(emptyList) UNLESS(emptyList=PyList_New(0)) return NULL;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(-1 != (l=PyList_Size(self->data))) return NULL;
l--;
UNLESS(r=PySequence_GetItem(self->data,l)) return NULL;
UNLESS(-1 != PyList_SetSlice(self->data,l,l+1,emptyList)) goto err;
return r;
err:
Py_DECREF(r);
return NULL;
}
static PyObject *
MM__init__(self, args)
MMobject *self;
PyObject *args;
{
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
UNLESS(self->data=PyList_New(0)) goto err;
Py_INCREF(Py_None);
return Py_None;
err:
Py_DECREF(self);
return NULL;
}
static struct PyMethodDef MM_methods[] = {
{"__init__", (PyCFunction)MM__init__, 1,
"__init__() -- Create a new empty multi-mapping"},
{"push", (PyCFunction) MM_push, 1,
"push(mapping_object) -- Add a data source"},
{"pop", (PyCFunction) MM_pop, 1,
"pop() -- Remove and return the last data source added"},
{NULL, NULL} /* sentinel */
};
static void
MM_dealloc(self)
MMobject *self;
{
Py_XDECREF(self->data);
PyMem_DEL(self);
}
static PyObject *
MM_getattr(self, name)
MMobject *self;
char *name;
{
return Py_FindMethod(MM_methods, (PyObject *)self, name);
}
static int
MM_length(self)
MMobject *self;
{
long l=0, el, i;
PyObject *e=0;
UNLESS(-1 != (i=PyList_Size(self->data))) return -1;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
UNLESS(-1 != (el=PyObject_Length(e))) return -1;
l+=el;
}
return l;
}
static PyObject *
MM_subscript(self, key)
MMobject *self;
PyObject *key;
{
long i;
PyObject *e;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while(--i >= 0)
{
e=PyList_GetItem(self->data,i);
if(e=PyObject_GetItem(e,key)) return e;
PyErr_Clear();
}
PyErr_SetObject(PyExc_KeyError,key);
return NULL;
}
static PyMappingMethods MM_as_mapping = {
(inquiry)MM_length, /*mp_length*/
(binaryfunc)MM_subscript, /*mp_subscript*/
(objobjargproc)NULL, /*mp_ass_subscript*/
};
/* -------------------------------------------------------- */
static char MMtype__doc__[] =
"MultiMapping -- Combine multiple mapping objects for lookup"
;
static PyExtensionClass MMtype = {
PyObject_HEAD_INIT(&PyType_Type)
0, /*ob_size*/
"MultMapping", /*tp_name*/
sizeof(MMobject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)MM_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)MM_getattr, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&MM_as_mapping, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
/* Space for future expansion */
0L,0L,0L,0L,
MMtype__doc__, /* Documentation string */
METHOD_CHAIN(MM_methods)
};
static struct PyMethodDef MultiMapping_methods[] = {
{NULL, NULL} /* sentinel */
};
void
initMultiMapping()
{
PyObject *m, *d;
m = Py_InitModule4(
"MultiMapping", MultiMapping_methods,
"MultiMapping -- Wrap multiple mapping objects for lookup",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
if (PyErr_Occurred())
Py_FatalError("can't initialize module MultiMapping");
}
This version includes 'ExtensionClass.h'. The two declarations of
'MMtype' have been changed from 'PyTypeObject' to 'PyExtensionClass'.
The 'METHOD_CHAIN' macro has been used to add methods to the end of
the definition for 'MMtype'. The module function, newMMobject has
been replaced by the 'MMtype' method, 'MM__init__'. Note that this
method does not create or return a new object. Finally, the lines::
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"MultiMapping",MMtype);
Have been added to both initialize the extension class and to export
it in the module dictionary.
To use this module, compile, link, and import it as with any other
extension module. The following python code illustrates the
module's use::
from MultiMapping import MultiMapping
m=MultiMapping()
m.push({'spam':1, 'eggs':2})
m.push({'spam':3, 'ham':4})
m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error
Creating the 'MultiMapping' object took three steps, one to create
an empty 'MultiMapping', and two to add mapping objects to it. We
might wish to simplify the process of creating MultiMapping
objects by providing a constructor that takes source mapping
objects as parameters. We can do this by sub-classing MultiMapping
in Python::
from MultiMapping import MultiMapping
class ExtendedMultiMapping(MultiMapping):
def __init__(self,*data):
MultiMapping.__init__(self)
for d in data: self.push(d)
m=ExtendedMultiMapping({'spam':1, 'eggs':2}, {'spam':3, 'ham':4})
m['spam'] # returns 3
m['ham'] # returns 4
m['foo'] # raises a key error
Note that the source file included in the ExtensionClass
distribution has numerous enhancements beyond the version shown in
this document.
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import os,sys
from StructuredText.StructuredText import HTML
if len(sys.argv)>1:
files = sys.argv[1:]
else:
files = os.listdir('.')
files = filter(lambda x: x.endswith('.stx'), files)
for f in files:
data = open(f,'r').read()
html = HTML(data)
outfile = f.replace('.stx','.ref')
open(outfile,'w').write(html)
<html>
<head>
<title>Small Trials for Structured Text Formatting</title>
</head>
<body>
<h1>Small Trials for Structured Text Formatting</h1>
<p> This paragraph should be preceded by a level 1 header. It should
not, itself, be made into a header, just a regular paragraph.</p>
<p> Here are a few presentation styles, in a list <a href="#ref1">[1]</a>:</p>
<ul>
<li>A word: <em>emphasized</em>.</li>
<li>A word: <u>underlined</u>.</li>
<li>A word <strong>strong</strong>.</li>
<li>An inline example: <code>1+2</code>.</li>
<li>Another example with a different format:
``x='spam''' or ``y='spam''' or ``<dtml-var spam>'<code>.</code><p> We can use expressions in the DTML var tag as
in ``<dtml-var "x+'.txt'">''</p>
</li>
<li>A mult-line example:
<pre>
blah
*foo bar*
&lt;dtml-var yeha&gt;
</pre>
</li>
</ul>
<p><a name="ref1">[1]</a> (The referring text should be a paragraph, not a header, and
should contain a reference to this footnote, footnote "<a href="#ref1">[1]</a>".)<p> Some hrefs, in a definition list:</p>
<dl>
<dt> <u>Regular</u></dt>
<dd><a href="http://www.zope.org">http://www.zope.org/</a></dd>
<dt> <u>W/trailing punctuation</u></dt>
<dd><a href="http://www.zope.org">http://www.zope.org/</a>.</dd>
<dt> <u>W protocol implicit</u></dt>
<dd><a href=":locallink">locallink</a></dd>
<dt> <u>W protocol implicit</u>, alternate</dt>
<dd>"locallink", :locallink</dd>
</dl>
<p> |||| A Simple Two-column Table ||
|| Column A || Column B ||
|| Apples || Oranges ||</p>
</p>
</body>
</html>
Small Trials for Structured Text Formatting
This paragraph should be preceded by a level 1 header. It should
not, itself, be made into a header, just a regular paragraph.
Here are a few presentation styles, in a list [1]:
- A word: *emphasized*.
- A word: _underlined_.
- A word **strong**.
- An inline example: '1+2'.
- Another example with a different format:
``x='spam''' or ``y='spam''' or ``<dtml-var spam>''.'
We can use expressions in the DTML var tag as
in ``<dtml-var "x+'.txt'">''
- A mult-line example::
blah
*foo bar*
<dtml-var yeha>
.. [1] (The referring text should be a paragraph, not a header, and
should contain a reference to this footnote, footnote "[1]".)
Some hrefs, in a definition list:
_Regular_ -- "http://www.zope.org/":http://www.zope.org
_W/trailing punctuation_ -- "http://www.zope.org/":http://www.zope.org.
_W protocol implicit_ -- "locallink"::locallink
_W protocol implicit_, alternate -- "locallink", :locallink
|||| A Simple Two-column Table ||
|| Column A || Column B ||
|| Apples || Oranges ||
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<p> For instance:
<pre>
&lt;table border="0"&gt;
&lt;tr&gt;&lt;td&gt;blabla&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
</pre>
</p>
</body>
</html>
Test
For instance::
<table border="0">
<tr><td>blabla</td></tr>
</table>
<html>
<head>
<title>Extension Class</title>
</head>
<body>
<h1>Extension Class</h1>
<p> <a href="COPYRIGHT.html">Copyright (C) 1996-1998, Digital Creations</a>.</p>
<p> A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:</p>
<ul>
<li>They can be sub-classed in python,</li>
<li>They provide access to method documentation strings, and</li>
<li>They can be used to directly create new instances.</li>
</ul>
<p> Extension classes provide additional extensions to class and
instance semantics, including:</p>
<ul>
<li>A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and <a href="Acquisition.html">environmental acquisition</a>.</li>
<li>A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.</li>
<li>A protocol for class initialization that supports execution of a
special <code>__class_init__</code> method after a class has been
initialized. </li>
</ul>
<p> Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models. </p>
<h2> Releases</h2>
<p> The current release is <a href="ExtensionClass-1.2.tar.gz">1.2</a>,
To find out what's changed in this release,
see the <a href="release.html">release notes</a>.</p>
<p> Documentation is available <a href="ExtensionClass.html">on-line</a>.</p>
<h3> Windows Binaries</h3>
<p> A win32 binary release, <a href="ec12.zip">ec12.zip</a>, is available. This
release includes all of the ExtensionClass modules built as
Windows extension modules (.pyd) files. These were built for
Python 1.5.1 using Microsoft Visual C++ 5.0 in "Release" mode.</p>
</body>
</html>
Extension Class
"Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
A lightweight mechanism has been developed for making Python
extension types more class-like. Classes can be developed in an
extension language, such as C or C++, and these classes can be
treated like other python classes:
- They can be sub-classed in python,
- They provide access to method documentation strings, and
- They can be used to directly create new instances.
Extension classes provide additional extensions to class and
instance semantics, including:
- A protocol for accessing subobjects "in the context of" their
containers. This is used to implement custom method types
and "environmental acquisition":Acquisition.html.
- A protocol for overriding method call semantics. This is used
to implement "synchonized" classes and could be used to
implement argument type checking.
- A protocol for class initialization that supports execution of a
special '__class_init__' method after a class has been
initialized.
Extension classes illustrate how the Python class mechanism can be
extended and may provide a basis for improved or specialized class
models.
Releases
The current release is "1.2":ExtensionClass-1.2.tar.gz,
To find out what's changed in this release,
see the "release notes":release.html.
Documentation is available "on-line":ExtensionClass.html.
Windows Binaries
A win32 binary release, "ec12.zip":ec12.zip, is available. This
release includes all of the ExtensionClass modules built as
Windows extension modules (.pyd) files. These were built for
Python 1.5.1 using Microsoft Visual C++ 5.0 in "Release" mode.
<html>
<body>
<table border="1" cellpadding="2">
<tr>
<th colspan="1" align="left" valign="middle"><p> Function </p>
</th>
<th colspan="1" align="left" valign="middle"><p> Documentation </p>
</th>
</tr>
<tr>
<td colspan="1" align="left" valign="top"><p> <code>__str__</code> </p>
</td>
<td colspan="1" align="left" valign="middle"><p> This method converts the
the object to a string. </p>
<ul>
<li>Blah </li>
<li>Blaf <table border="1" cellpadding="2">
<tr>
<th colspan="1" align="center" valign="top"><p> Name </p>
</th>
<th colspan="1" align="left" valign="middle"><p> Favorite
Color </p>
</th>
</tr>
<tr>
<td colspan="1" align="left" valign="middle"><p> Jim </p>
</td>
<td colspan="1" align="center" valign="middle"><p> Red </p>
</td>
</tr>
<tr>
<td colspan="1" align="left" valign="middle"><p> John </p>
</td>
<td colspan="1" align="center" valign="middle"><p> Blue </p>
</td>
</tr>
</table>
</li>
</ul>
</td>
</tr>
</table>
<table border="1" cellpadding="2">
<tr>
<td colspan="3" align="left" valign="middle"><p> This should give a row with colspan 3 </p>
</td>
</tr>
<tr>
<td colspan="1" align="left" valign="middle"><p> Col 1 </p>
</td>
<td colspan="1" align="center" valign="middle"><p> Col 2 </p>
</td>
<td colspan="1" align="center" valign="middle"><p> Col 3 </p>
</td>
</tr>
<tr>
<td colspan="1" align="left" valign="middle"><p> Col 1 </p>
</td>
<td colspan="2" align="center" valign="middle"><p> Col 2 </p>
</td>
</tr>
<tr>
<td colspan="2" align="left" valign="middle"><p> Col 1 </p>
</td>
<td colspan="1" align="center" valign="middle"><p> Col 2 </p>
</td>
</tr>
</table>
</body>
</html>
|-------------------------------------------------|
| Function | Documentation |
|=================================================|
| '__str__' | This method converts the |
| | the object to a string. |
| | |
| | - Blah |
| | |
| | - Blaf |
| | |
| | |--------------------------| |
| | | Name | Favorite | |
| | | | Color | |
| | |==========================| |
| | | Jim | Red | |
| | |--------------------------| |
| | | John | Blue | |
| | |--------------------------| |
|-------------------------------------------------|
|---------------------------------------|
| This should give a row with colspan 3 |
|---------------------------------------|
| Col 1 | Col 2 | Col 3 |
|---------------------------------------|
| Col 1 | Col 2 |
|---------------------------------------|
| Col 1 | Col 2 |
|---------------------------------------|
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
# This file is needed to make this a package.
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import zope.deprecation
from StructuredText import ST
from StructuredText import DocumentClass
from StructuredText import ClassicDocumentClass
from StructuredText import StructuredText
from StructuredText import HTMLClass
from StructuredText.StructuredText import HTML
import sys, os, unittest, cStringIO
from types import UnicodeType
from OFS import ndiff
"""
This tester first ensures that all regression files
can at least parse all files.
Secondly, the tester will compare the output between
StructuredText and ClassicDocumentClass->HTMLClass
to help ensure backwards compatability.
"""
package_dir = os.path.split(ST.__file__)[0]
regressions=os.path.join(package_dir, 'regressions')
files = ['index.stx','Acquisition.stx','ExtensionClass.stx',
'MultiMapping.stx','examples.stx','Links.stx','examples1.stx',
'table.stx','InnerLinks.stx']
def readFile(dirname,fname):
myfile = open(os.path.join(dirname, fname),"r")
lines = myfile.readlines()
myfile.close()
return ''.join(lines)
class StructuredTextTests(unittest.TestCase):
def setUp(self):
zope.deprecation.__show__.off()
def tearDown(self):
zope.deprecation.__show__.on()
def testStructuredText(self):
""" testing StructuredText """
for f in files:
raw_text = readFile(regressions,f)
assert StructuredText.StructuredText(raw_text),\
'StructuredText failed on %s' % f
assert StructuredText.StructuredText(unicode(raw_text)),\
'StructuredText failed on Unicode %s' % f
def testStructuredTextNG(self):
""" testing StructuredTextNG """
for f in files:
raw_text = readFile(regressions,f)
assert ST.StructuredText(raw_text),\
'StructuredText failed on %s' % f
assert ST.StructuredText(unicode(raw_text)),\
'StructuredText failed on Unicode %s' % f
def testDocumentClass(self):
""" testing DocumentClass"""
for f in files:
Doc = DocumentClass.DocumentClass()
raw_text = readFile(regressions,f)
text = ST.StructuredText(raw_text)
assert Doc(text),\
'DocumentClass failed on %s' % f
def testClassicDocumentClass(self):
""" testing ClassicDocumentClass"""
for f in files:
Doc = ClassicDocumentClass.DocumentClass()
raw_text = readFile(regressions,f)
text = ST.StructuredText(raw_text)
assert Doc(text),\
'ClassicDocumentClass failed on %s' % f
def testClassicHTMLDocumentClass(self):
""" testing HTML ClassicDocumentClass"""
for f in files:
Doc = ClassicDocumentClass.DocumentClass()
HTML = HTMLClass.HTMLClass()
raw_text = readFile(regressions,f)
text = Doc(ST.StructuredText(raw_text))
assert HTML(text),\
'HTML ClassicDocumentClass failed on %s' % f
def testRegressionsTests(self):
""" HTML regression test """
for f in files:
Doc = DocumentClass.DocumentClass()
HTML = HTMLClass.HTMLClass()
raw_text = readFile(regressions,f)
text = Doc(ST.StructuredText(raw_text))
html = HTML(text)
reg_fname = f.replace('.stx','.ref')
reg_html = readFile(regressions , reg_fname)
if reg_html.strip()!= html.strip():
IO = cStringIO.StringIO()
oldStdout = sys.stdout
sys.stdout = IO
try:
open('_tmpout','w').write(html)
ndiff.fcompare(os.path.join(regressions,reg_fname),
'_tmpout')
os.unlink('_tmpout')
finally:
sys.stdout = oldStdout
raise AssertionError, \
'HTML regression test failed on %s\nDiff:\n%s\n' % (f,
IO.getvalue())
class BasicTests(unittest.TestCase):
def setUp(self):
zope.deprecation.__show__.off()
def tearDown(self):
zope.deprecation.__show__.on()
def _test(self,stxtxt , expected):
if not isinstance(stxtxt, UnicodeType):
res = HTML(stxtxt,level=1,header=0)
if not expected in res:
print "Text: ",stxtxt
print "Converted:",res
print "Expected: ",expected
raise AssertionError,"basic test failed for '%s'" % stxtxt
if isinstance(stxtxt, UnicodeType):
ustxtxt = stxtxt
else:
ustxtxt = unicode(stxtxt)
res = HTML(ustxtxt,level=1,header=0)
if not expected in res:
print "Text: ",stxtxt.encode('latin-1')
print "Converted:",res.encode('latin-1')
print "Expected: ",expected.encode('latin-1')
raise AssertionError, ("basic test failed for Unicode '%s'"
% stxtxt)
def testUnderline(self):
self._test("xx _this is html_ xx",
"xx <u>this is html</u> xx")
def testUnderline1(self):
self._test("xx _this is html_",
"<u>this is html</u>")
def testEmphasis(self):
self._test("xx *this is html* xx",
"xx <em>this is html</em> xx")
def testStrong(self):
self._test("xx **this is html** xx",
"xx <strong>this is html</strong> xx")
def testUnderlineThroughoutTags(self):
self._test('<a href="index_html">index_html</a>',
'<a href="index_html">index_html</a>')
def testUnderscoresInLiteral1(self):
self._test("def __init__(self)",
"def __init__(self)")
def testUnderscoresInLiteral2(self):
self._test("this is '__a_literal__' eh",
"<code>__a_literal__</code>")
def testUnderlinesWithoutWithspaces(self):
self._test("Zopes structured_text is sometimes a night_mare",
"Zopes structured_text is sometimes a night_mare")
def testAsterisksInLiteral(self):
self._test("this is a '*literal*' eh",
"<code>*literal*</code>")
def testDoubleAsterisksInLiteral(self):
self._test("this is a '**literal**' eh",
"<code>**literal**</code>")
def testLinkInLiteral(self):
self._test("this is a '\"literal\":http://www.zope.org/.' eh",
'<code>"literal":http://www.zope.org/.</code>')
def testLink(self):
self._test('"foo":http://www.zope.org/foo/bar',
'<p><a href="http://www.zope.org/foo/bar">foo</a></p>')
self._test('"foo":http://www.zope.org/foo/bar/%20x',
'<p><a href="http://www.zope.org/foo/bar/%20x">foo</a></p>')
self._test('"foo":http://www.zope.org/foo/bar?arg1=1&arg2=2',
'<p><a href="http://www.zope.org/foo/bar?arg1=1&arg2=2">foo</a></p>')
self._test('"foo bar":http://www.zope.org/foo/bar',
'<p><a href="http://www.zope.org/foo/bar">foo bar</a></p>')
self._test('"[link goes here]":http://www.zope.org/foo/bar',
'<p><a href="http://www.zope.org/foo/bar">[link goes here]</a></p>')
self._test('"[Dad\'s car]":http://www.zope.org/foo/bar',
'<p><a href="http://www.zope.org/foo/bar">[Dad\'s car]</a></p>')
def testImgLink(self):
self._test('"foo":img:http://www.zope.org/bar.gif',
'<img src="http://www.zope.org/bar.gif" alt="foo" />')
self._test('"foo":img:http://www.zope.org:8080/bar.gif',
'<img src="http://www.zope.org:8080/bar.gif" alt="foo" />')
self._test('"foo":img:http://www.zope.org:8080/foo/bar?arg=1',
'<img src="http://www.zope.org:8080/foo/bar?arg=1" alt="foo" />')
self._test('"foo":img:http://www.zope.org:8080/foo/b%20ar?arg=1',
'<img src="http://www.zope.org:8080/foo/b%20ar?arg=1" alt="foo" />')
self._test('"foo bar":img:http://www.zope.org:8080/foo/bar',
'<img src="http://www.zope.org:8080/foo/bar" alt="foo bar" />')
self._test('"[link goes here]":img:http://www.zope.org:8080/foo/bar',
'<img src="http://www.zope.org:8080/foo/bar" alt="[link goes here]" />')
self._test('"[Dad\'s new car]":img:http://www.zope.org:8080/foo/bar',
'<img src="http://www.zope.org:8080/foo/bar" alt="[Dad\'s new car]" />')
def XXXtestUnicodeContent(self):
# This fails because ST uses the default locale to get "letters"
# whereas it should use \w+ and re.U if the string is Unicode.
#self._test(u"h\xe9 **y\xe9** xx",
# u"h\xe9 <strong>y\xe9</strong> xx")
pass
def test_suite():
suite = unittest.TestSuite()
suite.addTest( unittest.makeSuite( StructuredTextTests ) )
suite.addTest( unittest.makeSuite( BasicTests ) )
return suite
def main():
unittest.TextTestRunner().run(test_suite())
if __name__ == '__main__':
main()
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Provide a thread-safe interface to regex
"""
import regex, regsub #, Sync
from regex import *
from regsub import split, sub, gsub, splitx, capwords
try:
import thread
except:
class allocate_lock:
def acquire(*args): pass
def release(*args): pass
else:
class SafeFunction:
_l=thread.allocate_lock()
_a=_l.acquire
_r=_l.release
def __init__(self, f):
self._f=f
def __call__(self, *args, **kw):
self._a()
try: return self._f(*args, **kw)
finally: self._r()
split=SafeFunction(split)
sub=SafeFunction(sub)
gsub=SafeFunction(gsub)
splitx=SafeFunction(splitx)
capwords=SafeFunction(capwords)
allocate_lock=thread.allocate_lock
class compile:
_r=None
groupindex=None
def __init__(self, *args):
self._r=r=regex(*compile, **args)
self._init(r)
def _init(self, r):
lock=allocate_lock()
self.__a=lock.acquire
self.__r=lock.release
self.translate=r.translate
self.givenpat=r.givenpat
self.realpat=r.realpat
def match(self, string, pos=0):
self.__a()
try: return self._r.match(string, pos)
finally: self.__r()
def search(self, string, pos=0):
self.__a()
try: return self._r.search(string, pos)
finally: self.__r()
def search_group(self, str, group, pos=0):
"""Search a string for a pattern.
If the pattern was not found, then None is returned,
otherwise, the location where the pattern was found,
as well as any specified group are returned.
"""
self.__a()
try:
r=self._r
l=r.search(str, pos)
if l < 0: return None
return l, r.group(*group)
finally: self.__r()
def match_group(self, str, group, pos=0):
"""Match a pattern against a string
If the string does not match the pattern, then None is
returned, otherwise, the length of the match, as well
as any specified group are returned.
"""
self.__a()
try:
r=self._r
l=r.match(str, pos)
if l < 0: return None
return l, r.group(*group)
finally: self.__r()
def search_regs(self, str, pos=0):
"""Search a string for a pattern.
If the pattern was not found, then None is returned,
otherwise, the 'regs' attribute of the expression is
returned.
"""
self.__a()
try:
r=self._r
r.search(str, pos)
return r.regs
finally: self.__r()
def match_regs(self, str, pos=0):
"""Match a pattern against a string
If the string does not match the pattern, then None is
returned, otherwise, the 'regs' attribute of the expression is
returned.
"""
self.__a()
try:
r=self._r
r.match(str, pos)
return r.regs
finally: self.__r()
class symcomp(compile):
def __init__(self, *args):
self._r=r=regex.symcomp(*args)
self._init(r)
self.groupindex=r.groupindex
TAL changes
This file contains change information for the current release.
Change information for previous versions can be found in the
file HISTORY.txt.
Version 1.5.0
Features Added
- Line and column numbers are added to more exceptions.
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Dummy TALES engine so that I can test out the TAL implementation.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.dummyengine', '2.12')
from zope.tal.dummyengine import DummyTranslationDomain as DummyDomain
class DummyTranslationService:
def translate(self, domain, msgid, mapping=None, context=None,
target_language=None, default=None):
return self.getDomain(domain).translate(msgid, mapping, context,
target_language,
default=default)
def getDomain(self, domain):
return DummyDomain()
TAL history
This file contains change information for previous versions.
Change information for the current release can be found
in the file CHANGES.txt.
Version 1.4.0
Features Added
- Added TAL statement: omit_tag="[<boolean expr>]" replaces
the statement tag with its contents if the boolean
expression is true or omitted.
- The TAL and METAL namespaces can be applied to tag names,
tags in these namespaces are removed from rendered output
(leaving the contents in place, as with omit_tag)
whenever attributes in these namespaces would be, and
tag attributes without explicit namespaces default to the
tag's namespace (per XML spec).
Version 1.3.3
Bugs Fixed
- tal:atributes was creating stray attributes in METAL
expansion, and there was no unit test for this behavior.
- tal:attributes parsing was not catching badly malformed
values, and used "print" instead of raising exceptions.
Version 1.3.2
Features Added
- Adopted Zope-style CHANGES.txt and HISTORY.txt
- Improved execution performance
- Added simple ZPT vs. TAL vs. DTML benchmarks, run by markbench.py
Version 1.3.0
Features Added
- New builtin variable 'attrs'.
Bug Fixed
- Nested macros were not working correctly.
Version 1.2.0
Features Added
- The 'if' path modifier can cancel any TAL action.
Bug Fixed
- tal:attributes inserted empty attributes into source.
Version 1.1.0
Features Added
- TAL does not try to parse replacement structural text.
- Changed tests to match TAL's omitted attributes.
Version 1.0.0
- Various minor bugs fixed
Version 1.0.0b1
- All functionality described in the Project Wiki is implemented
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Parse HTML and compile to TALInterpreter intermediate code.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.htmltalparser', '2.12')
"""Interface that a TALES engine provides to the METAL/TAL implementation."""
import zope.deferredimport
zope.deferredimport.deprecatedFrom(
"The TAL implementation has moved to zope.tal. Import expression "
"interfaces from zope.tal.interfaces. The old references will be "
"gone in Zope 2.12.",
'zope.tal.interfaces'
'ITALExpressionCompiler', 'ITALExpressionEngine', 'ITALExpressionErrorInfo'
)
TAL - Template Attribute Language
---------------------------------
This is an implementation of TAL, the Zope Template Attribute
Language. For TAL, see the Zope Presentation Templates ZWiki:
http://dev.zope.org/Wikis/DevSite/Projects/ZPT/FrontPage
It is not a Zope product nor is it designed exclusively to run inside
of Zope, but if you have a Zope checkout that includes
Products/ParsedXML, its Expat parser will be used.
Prerequisites
-------------
You need:
- A recent checkout of Zope2; don't forget to run the wo_pcgi.py
script to compile everything. (See above -- this is now optional.)
- A recent checkout of the Zope2 product ParsedXML, accessible
throught <Zope2>/lib/python/Products/ParsedXML; don't forget to run
the setup.py script to compiles Expat. (Again, optional.)
- Python 1.5.2; the driver script refuses to work with other versions
unless you specify the -n option; this is done so that I don't
accidentally use Python 2.x features.
- Create a .path file containing proper module search path; it should
point the <Zope2>/lib/python directory that you want to use.
How To Play
-----------
(Don't forget to edit .path, see above!)
The script driver.py takes an XML file with TAL markup as argument and
writes the expanded version to standard output. The filename argument
defaults to tests/input/test01.xml.
Regression test
---------------
There are unit test suites in the 'tests' subdirectory; these can be
run with tests/run.py. This should print the testcase names plus
progress info, followed by a final line saying "OK". It requires that
../unittest.py exists.
There are a number of test files in the 'tests' subdirectory, named
tests/input/test<number>.xml and tests/input/test<number>.html. The
Python script ./runtest.py calls driver.main() for each test file, and
should print "<file> OK" for each one. These tests are also run as
part of the unit test suites, so tests/run.py is all you need.
What's Here
-----------
DummyEngine.py simple-minded TALES execution engine
TALInterpreter.py class to interpret intermediate code
TALGenerator.py class to generate intermediate code
XMLParser.py base class to parse XML, avoiding DOM
TALParser.py class to parse XML with TAL into intermediate code
HTMLTALParser.py class to parse HTML with TAL into intermediate code
HTMLParser.py HTML-parsing base class
driver.py script to demonstrate TAL expansion
timer.py script to time various processing phases
setpath.py hack to set sys.path and import ZODB
__init__.py empty file that makes this directory a package
runtest.py Python script to run file-comparison tests
ndiff.py helper for runtest.py to produce diffs
tests/ drectory with test files and output
tests/run.py Python script to run all tests
Author and License
------------------
This code is written by Guido van Rossum (project lead), Fred Drake,
and Tim Peters. It is owned by Digital Creations and can be
redistributed under the Zope Public License.
TO DO
-----
(See also http://www.zope.org/Members/jim/ZPTIssueTracker .)
- Need to remove leading whitespace and newline when omitting an
element (either through tal:replace with a value of nothing or
tal:condition with a false condition).
- Empty TAL/METAL attributes are ignored: tal:replace="" is ignored
rather than causing an error.
- HTMLTALParser.py and TALParser.py are silly names. Should be
HTMLTALCompiler.py and XMLTALCompiler.py (or maybe shortened,
without "TAL"?)
- Should we preserve case of tags and attribute names in HTML?
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Common definitions used by TAL and METAL compilation an transformation.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.taldefs', '2.12')
import zope.deferredimport
zope.deferredimport.deprecated(
"TALESError has been renamed TALExpressionError and should be "
"imported from zope.tal.taldefs. This reference will be gone in "
"Zope 2.12.",
TALESError = 'zope.tal.taldefs.TALExpressionError'
)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Code generator for TALInterpreter intermediate code.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.talgenerator', '2.12')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interpreter for a pre-compiled TAL program.
BBB 2005/05/01 -- to be removed after 12 months
$Id$
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.talinterpreter', '2.12')
import zope.deferredimport
zope.deferredimport.deprecated(
"'interpolate' has moved to zope.i18n. This reference will be gone "
"in Zope 2.12.",
interpolate = 'zope.i18n:interpolate'
)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Parse XML and compile to TALInterpreter intermediate code.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.talparser', '2.12')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Translation context object for the TALInterpreter's I18N support.
BBB 2005/05/01 -- to be removed after 12 months
$Id$
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.translationcontext', '2.12')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Generic expat-based XML parser base class.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.xmlparser', '2.12')
import xml.parsers.expat
XMLParseError = xml.parsers.expat.ExpatError
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Template Attribute Language package """
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<dtml-in r8>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
</dtml-in>
<dtml-in r8>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
</dtml-in>
<dtml-in r2>
<dtml-in r2>
<dtml-in r2>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
</dtml-in>
</dtml-in>
</dtml-in>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
<dtml-in r64>
<td bgcolor="white">&dtml-x0;</td>
<td bgcolor="white">&dtml-x1;</td>
<td bgcolor="white">&dtml-x2;</td>
<td bgcolor="white">&dtml-x3;</td>
<td bgcolor="white">&dtml-x4;</td>
<td bgcolor="white">&dtml-x5;</td>
<td bgcolor="white">&dtml-x6;</td>
<td bgcolor="white">&dtml-x7;</td>
</dtml-in>
<dtml-in r64>
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
</dtml-in>
<dtml-in r64>
<td bgcolor="white">&dtml-x0;</td>
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
</dtml-in>
<dtml-in r8>
<dtml-let y0=x0 y1=x1 y2=x2 y3=x3 y4=x4 y5=x5 y6=x6 y7=x7>
<td bgcolor="white">&dtml-y0;</td>
<td bgcolor="white">&dtml-y1;</td>
<td bgcolor="white">&dtml-y2;</td>
<td bgcolor="white">&dtml-y3;</td>
<td bgcolor="white">&dtml-y4;</td>
<td bgcolor="white">&dtml-y5;</td>
<td bgcolor="white">&dtml-y6;</td>
<td bgcolor="white">&dtml-y7;</td>
</dtml-let>
</dtml-in>
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<dtml-in tal:repeat="r r8">
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
</dtml-in>
<dtml-in tal:repeat="r r8">
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
</dtml-in>
<dtml-in tal:repeat="r r2">
<dtml-in tal:repeat="r r2">
<dtml-in tal:repeat="r r2">
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
</dtml-in>
</dtml-in>
</dtml-in>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<td bgcolor="white"><span tal:replace="x0"></span></td>
<td bgcolor="white"><span tal:replace="x1"></span></td>
<td bgcolor="white"><span tal:replace="x2"></span></td>
<td bgcolor="white"><span tal:replace="x3"></span></td>
<td bgcolor="white"><span tal:replace="x4"></span></td>
<td bgcolor="white"><span tal:replace="x5"></span></td>
<td bgcolor="white"><span tal:replace="x6"></span></td>
<td bgcolor="white"><span tal:replace="x7"></span></td>
<dtml-in tal:repeat="r r64">
<td bgcolor="white" tal:content="x0"></td>
<td bgcolor="white" tal:content="x1"></td>
<td bgcolor="white" tal:content="x2"></td>
<td bgcolor="white" tal:content="x3"></td>
<td bgcolor="white" tal:content="x4"></td>
<td bgcolor="white" tal:content="x5"></td>
<td bgcolor="white" tal:content="x6"></td>
<td bgcolor="white" tal:content="x7"></td>
</dtml-in>
<dtml-in tal:repeat="r r64">
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
</dtml-in>
<dtml-in tal:repeat="r r64">
<td bgcolor="white" tal:content="x0"></td>
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
A large chunk of text to be repeated.
</dtml-in>
<dtml-in tal:repeat="r r8">
<span tal:define="y0 x0;y1 x1;y2 x2;y3 x3;y4 x4;y5 x5;y6 x6;y7 x7">
<td bgcolor="white" tal:content="y0"></td>
<td bgcolor="white" tal:content="y1"></td>
<td bgcolor="white" tal:content="y2"></td>
<td bgcolor="white" tal:content="y3"></td>
<td bgcolor="white" tal:content="y4"></td>
<td bgcolor="white" tal:content="y5"></td>
<td bgcolor="white" tal:content="y6"></td>
<td bgcolor="white" tal:content="y7"></td>
</span>
</dtml-in>
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Driver program to test METAL and TAL implementation.
Usage: driver.py [options] [file]
Options:
-h / --help
Print this message and exit.
-H / --html
-x / --xml
Explicitly choose HTML or XML input. The default is to automatically
select based on the file extension. These options are mutually
exclusive.
-l
Lenient structure insertion.
-m
Macro expansion only
-s
Print intermediate opcodes only
-t
Leave TAL/METAL attributes in output
-i
Leave I18N substitution strings un-interpolated.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.driver', '2.12')
if __name__ == "__main__":
main()
#! /usr/bin/env python
# This software is subject to the provisions of the Zope Public License,
# Version 1.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
'''Run benchmarks of TAL vs. DTML'''
try:
import warnings
except ImportError:
pass
else:
warnings.filterwarnings("ignore", category=DeprecationWarning)
import os
os.environ['NO_SECURITY'] = 'true'
import sys, time
if __name__ == "__main__":
import setpath
from DocumentTemplate.DT_HTML import HTMLFile
from HTMLTALParser import HTMLTALParser
from TALInterpreter import TALInterpreter
from DummyEngine import DummyEngine
from cStringIO import StringIO
def time_apply(f, args, kwargs, count):
for i in range(4):
f(*args, **kwargs)
r = [None] * count
t0 = time.clock()
for i in r:
pass
t1 = time.clock()
for i in r:
f(*args, **kwargs)
t = time.clock() - t1 - (t1 - t0)
return t / count
def time_zpt(fn, count):
from Products.PageTemplates.PageTemplate import PageTemplate
pt = PageTemplate()
pt.write(open(fn).read())
return time_apply(pt.pt_render, (), {'extra_context': data}, count)
def time_tal(fn, count):
p = HTMLTALParser()
p.parseFile(fn)
program, macros = p.getCode()
engine = DummyEngine(macros)
engine.globals = data
tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0,
tal=1, strictinsert=0)
return time_apply(tal, (), {}, count)
def time_dtml(fn, count):
html = HTMLFile(fn)
return time_apply(html, (), data, count)
def profile_zpt(fn, count, profiler):
from Products.PageTemplates.PageTemplate import PageTemplate
pt = PageTemplate()
pt.write(open(fn).read())
for i in range(4):
pt.pt_render(extra_context=data)
r = [None] * count
for i in r:
profiler.runcall(pt.pt_render, 0, data)
def profile_tal(fn, count, profiler):
p = HTMLTALParser()
p.parseFile(fn)
program, macros = p.getCode()
engine = DummyEngine(macros)
engine.globals = data
tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0,
tal=1, strictinsert=0)
for i in range(4):
tal()
r = [None] * count
for i in r:
profiler.runcall(tal)
tal_fn = 'benchmark/tal%.2d.html'
dtml_fn = 'benchmark/dtml%.2d.html'
def compare(n, count, profiler=None):
t1 = int(time_zpt(tal_fn % n, count) * 1000 + 0.5)
t2 = int(time_tal(tal_fn % n, count) * 1000 + 0.5)
t3 = int(time_dtml(dtml_fn % n, count) * 1000 + 0.5)
print '%.2d: %10s %10s %10s' % (n, t1, t2, t3)
if profiler:
profile_tal(tal_fn % n, count, profiler)
def main(count, profiler=None):
n = 1
print '##: %10s %10s %10s' % ('ZPT', 'TAL', 'DTML')
while os.path.isfile(tal_fn % n) and os.path.isfile(dtml_fn % n):
compare(n, count, profiler)
n = n + 1
data = {'x':'X', 'r2': range(2), 'r8': range(8), 'r64': range(64)}
for i in range(10):
data['x%s' % i] = 'X%s' % i
if __name__ == "__main__":
filename = "markbench.prof"
profiler = None
if len(sys.argv) > 1 and sys.argv[1] == "-p":
import profile
profiler = profile.Profile()
del sys.argv[1]
if len(sys.argv) > 1:
for arg in sys.argv[1:]:
compare(int(arg), 25, profiler)
else:
main(25, profiler)
if profiler is not None:
profiler.dump_stats(filename)
import pstats
p = pstats.Stats(filename)
p.strip_dirs()
p.sort_stats('time', 'calls')
try:
p.print_stats(20)
except IOError, e:
if e.errno != errno.EPIPE:
raise
"""Shared support for scanning document type declarations in HTML and XHTML."""
import re, string
_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match
_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match
del re
class ParserBase:
"""Parser base class which provides some common support methods used
by the SGML/HTML and XHTML parsers."""
def reset(self):
self.lineno = 1
self.offset = 0
def getpos(self):
"""Return current line number and offset."""
return self.lineno, self.offset
def error(self, message):
"""Return an error, showing current line number and offset.
Concrete subclasses *must* override this method.
"""
raise NotImplementedError
# Internal -- update line number and offset. This should be
# called for each piece of data exactly once, in order -- in other
# words the concatenation of all the input strings to this
# function should be exactly the entire input.
def updatepos(self, i, j):
if i >= j:
return j
rawdata = self.rawdata
nlines = rawdata.count("\n", i, j)
if nlines:
self.lineno = self.lineno + nlines
pos = rawdata.rindex("\n", i, j) # Should not fail
self.offset = j-(pos+1)
else:
self.offset = self.offset + j-i
return j
_decl_otherchars = ''
# Internal -- parse declaration (for use by subclasses).
def parse_declaration(self, i):
# This is some sort of declaration; in "HTML as
# deployed," this should only be the document type
# declaration ("<!DOCTYPE html...>").
rawdata = self.rawdata
import sys
j = i + 2
assert rawdata[i:j] == "<!", "unexpected call to parse_declaration"
if rawdata[j:j+1] in ("-", ""):
# Start of comment followed by buffer boundary,
# or just a buffer boundary.
return -1
# in practice, this should look like: ((name|stringlit) S*)+ '>'
n = len(rawdata)
decltype, j = self._scan_name(j, i)
if j < 0:
return j
if decltype == "doctype":
self._decl_otherchars = ''
while j < n:
c = rawdata[j]
if c == ">":
# end of declaration syntax
data = rawdata[i+2:j]
if decltype == "doctype":
self.handle_decl(data)
else:
self.unknown_decl(data)
return j + 1
if c in "\"'":
m = _declstringlit_match(rawdata, j)
if not m:
return -1 # incomplete
j = m.end()
elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
name, j = self._scan_name(j, i)
elif c in self._decl_otherchars:
j = j + 1
elif c == "[":
if decltype == "doctype":
j = self._parse_doctype_subset(j + 1, i)
else:
self.error("unexpected '[' char in declaration")
else:
self.error(
"unexpected %s char in declaration" % `rawdata[j]`)
if j < 0:
return j
return -1 # incomplete
# Internal -- scan past the internal subset in a <!DOCTYPE declaration,
# returning the index just past any whitespace following the trailing ']'.
def _parse_doctype_subset(self, i, declstartpos):
rawdata = self.rawdata
n = len(rawdata)
j = i
while j < n:
c = rawdata[j]
if c == "<":
s = rawdata[j:j+2]
if s == "<":
# end of buffer; incomplete
return -1
if s != "<!":
self.updatepos(declstartpos, j + 1)
self.error("unexpected char in internal subset (in %s)"
% `s`)
if (j + 2) == n:
# end of buffer; incomplete
return -1
if (j + 4) > n:
# end of buffer; incomplete
return -1
if rawdata[j:j+4] == "<!--":
j = self.parse_comment(j, report=0)
if j < 0:
return j
continue
name, j = self._scan_name(j + 2, declstartpos)
if j == -1:
return -1
if name not in ("attlist", "element", "entity", "notation"):
self.updatepos(declstartpos, j + 2)
self.error(
"unknown declaration %s in internal subset" % `name`)
# handle the individual names
meth = getattr(self, "_parse_doctype_" + name)
j = meth(j, declstartpos)
if j < 0:
return j
elif c == "%":
# parameter entity reference
if (j + 1) == n:
# end of buffer; incomplete
return -1
s, j = self._scan_name(j + 1, declstartpos)
if j < 0:
return j
if rawdata[j] == ";":
j = j + 1
elif c == "]":
j = j + 1
while j < n and rawdata[j] in string.whitespace:
j = j + 1
if j < n:
if rawdata[j] == ">":
return j
self.updatepos(declstartpos, j)
self.error("unexpected char after internal subset")
else:
return -1
elif c in string.whitespace:
j = j + 1
else:
self.updatepos(declstartpos, j)
self.error("unexpected char %s in internal subset" % `c`)
# end of buffer reached
return -1
# Internal -- scan past <!ELEMENT declarations
def _parse_doctype_element(self, i, declstartpos):
rawdata = self.rawdata
n = len(rawdata)
name, j = self._scan_name(i, declstartpos)
if j == -1:
return -1
# style content model; just skip until '>'
if '>' in rawdata[j:]:
return rawdata.find(">", j) + 1
return -1
# Internal -- scan past <!ATTLIST declarations
def _parse_doctype_attlist(self, i, declstartpos):
rawdata = self.rawdata
name, j = self._scan_name(i, declstartpos)
c = rawdata[j:j+1]
if c == "":
return -1
if c == ">":
return j + 1
while 1:
# scan a series of attribute descriptions; simplified:
# name type [value] [#constraint]
name, j = self._scan_name(j, declstartpos)
if j < 0:
return j
c = rawdata[j:j+1]
if c == "":
return -1
if c == "(":
# an enumerated type; look for ')'
if ")" in rawdata[j:]:
j = rawdata.find(")", j) + 1
else:
return -1
while rawdata[j:j+1].isspace():
j = j + 1
if not rawdata[j:]:
# end of buffer, incomplete
return -1
else:
name, j = self._scan_name(j, declstartpos)
c = rawdata[j:j+1]
if not c:
return -1
if c in "'\"":
m = _declstringlit_match(rawdata, j)
if m:
j = m.end()
else:
return -1
c = rawdata[j:j+1]
if not c:
return -1
if c == "#":
if rawdata[j:] == "#":
# end of buffer
return -1
name, j = self._scan_name(j + 1, declstartpos)
if j < 0:
return j
c = rawdata[j:j+1]
if not c:
return -1
if c == '>':
# all done
return j + 1
# Internal -- scan past <!NOTATION declarations
def _parse_doctype_notation(self, i, declstartpos):
name, j = self._scan_name(i, declstartpos)
if j < 0:
return j
rawdata = self.rawdata
while 1:
c = rawdata[j:j+1]
if not c:
# end of buffer; incomplete
return -1
if c == '>':
return j + 1
if c in "'\"":
m = _declstringlit_match(rawdata, j)
if not m:
return -1
j = m.end()
else:
name, j = self._scan_name(j, declstartpos)
if j < 0:
return j
# Internal -- scan past <!ENTITY declarations
def _parse_doctype_entity(self, i, declstartpos):
rawdata = self.rawdata
if rawdata[i:i+1] == "%":
j = i + 1
while 1:
c = rawdata[j:j+1]
if not c:
return -1
if c in string.whitespace:
j = j + 1
else:
break
else:
j = i
name, j = self._scan_name(j, declstartpos)
if j < 0:
return j
while 1:
c = self.rawdata[j:j+1]
if not c:
return -1
if c in "'\"":
m = _declstringlit_match(rawdata, j)
if m:
j = m.end()
else:
return -1 # incomplete
elif c == ">":
return j + 1
else:
name, j = self._scan_name(j, declstartpos)
if j < 0:
return j
# Internal -- scan a name token and the new position and the token, or
# return -1 if we've reached the end of the buffer.
def _scan_name(self, i, declstartpos):
rawdata = self.rawdata
n = len(rawdata)
if i == n:
return None, -1
m = _declname_match(rawdata, i)
if m:
s = m.group()
name = s.strip()
if (i + len(s)) == n:
return None, -1 # end of buffer
return name.lower(), m.end()
else:
self.updatepos(declstartpos, i)
self.error("expected name token")
#! /usr/bin/env python
# BBB 2005/05/01 -- to be removed after 12 months
import zope.deprecation
zope.deprecation.moved('zope.tal.ndiff', '2.12')
if __name__ == '__main__':
import sys
args = sys.argv[1:]
if "-profile" in args:
import profile, pstats
args.remove("-profile")
statf = "ndiff.pro"
profile.run("main(args)", statf)
stats = pstats.Stats(statf)
stats.strip_dirs().sort_stats('time').print_stats()
else:
main(args)
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Driver program to run METAL and TAL regression tests.
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.runtest', '2.12')
if __name__ == "__main__":
main()
# This software is subject to the provisions of the Zope Public License,
# Version 1.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
"""
Read a module search path from .path.
"""
import os
import sys
dir = os.path.dirname(__file__)
path = os.path.join(dir, ".path")
try:
f = open(path)
except IOError:
raise IOError, "Please edit .path to point to <Zope2/lib/python>"
else:
for line in f.readlines():
line = line.strip()
if line and line[0] != '#':
for dir in line.split(os.pathsep):
dir = os.path.expanduser(os.path.expandvars(dir))
if dir not in sys.path:
sys.path.append(dir)
import ZODB # Must import this first to initialize Persistence properly
# BBB 2005/05/01 -- to be removed after 12 months
import zope.deprecation
zope.deprecation.moved('zope.tal.talgettext', '2.12')
if __name__ == '__main__':
main()
"""Empty file to make this directory a Python package."""
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html>
<head>dadada</head>
<body xmlns:z="http://xml.zope.org/namespaces/tal" z:define="foo python:1">
<h1 z:condition="python:0">This title is not displayed</h1>
<h1 z:condition="python:1" z:content="str:This
Is
The
Replaced
Title">Title</h1>
<!-- test entity references -->
&nbsp;&HarryPotter;
<!-- examples adapted from TemplateAttributeLanguageSyntax -->
<span z:content="str:here/id"/>
<p z:define="x str:template/title; global five python:2+3;" z:content="text var:five"/>
<p z:repeat="car python:['honda', 'subaru', 'acura']">
<span z:replace="var:car"/>
</p>
<p xml:foo="bar">foo bar</p>
<!-- more examples -->
<ul>
<span z:repeat="car python:['honda', 'subaru', 'acura']">
<li z:content="var:car">Car Name</li>
</span>
</ul>
<!-- test attribute expansion -->
<a href="foo" z:attributes="href python:'http://python.org' ">python</a>
<a z:attributes="href python:'http://python.org' ">python</a>
<!-- test insert/replace structure -->
<span z:content="structure python:None" />
<span z:replace="structure python:None" />
<span z:define="global x str:&lt;h3&gt;Header Level 3&lt;/h3&gt;" />
<span z:define="global x python:'&amp;' + 'nbsp;;' + x" />
<span z:replace="structure x" />
<span z:content="structure x" />
</body>
</html>
<?xml version="1.0" ?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html>
<head>dadada</head>
<body xmlns:z="http://xml.zope.org/namespaces/tal" z:define="foo python:1">
<h1 z:condition="python:0">This title is not displayed</h1>
<h1 z:condition="python:1" z:content="str:This
Is
The
Replaced
Title">Title</h1>
<!-- test entity references -->
&nbsp;&HarryPotter;
<!-- examples adapted from TemplateAttributeLanguageSyntax -->
<span z:content="str:here/id"/>
<p z:define="x str:template/title; global five python:2+3;" z:content="text var:five"/>
<p z:repeat="car python:['honda', 'subaru', 'acura']">
<span z:replace="var:car"/>
</p>
<p xml:foo="bar">foo bar</p>
<!-- more examples -->
<ul>
<span z:repeat="car python:['honda', 'subaru', 'acura']">
<li z:content="var:car">Car Name</li>
</span>
</ul>
<!-- test attribute expansion -->
<a href="foo" z:attributes="href python:'http://python.org' ">python</a>
<a z:attributes="href python:'http://python.org' ">python</a>
<!-- test insert/replace structure -->
<span z:content="structure python:None" />
<span z:replace="structure python:None" />
<span z:define="global x str:&lt;h3&gt;Header Level 3&lt;/h3&gt;" />
<span z:define="global x python:'&amp;' + 'nbsp;;' + x" />
<span z:replace="structure x" />
<span z:content="structure x" />
</body>
</html>
<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
<foo:header xmlns:foo="whomping-willow" plain="guido" quote='"' apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
<manifest>
<document>
<name>sample1</name>
<description>a simple invoice</description>
</document>
</manifest>
</foo:header>
<body>
<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
<Header>
<InvoiceNumber>01786</InvoiceNumber>
<InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
<OrderNo>55377</OrderNo>
<OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
<CustomerPO>GJ03405</CustomerPO>
<ShipMethod>DAVE 1</ShipMethod>
<ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
<CustomerID>K5211(34)</CustomerID>
<SalesPersonCode>23</SalesPersonCode>
<TaxID>23</TaxID>
</Header>
<InvoiceTo>
<Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
<AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
<AddressLine>WESTLAKE ACCESS</AddressLine>
<City>NORTH BAY</City>
<PostCode>L8B1O5</PostCode>
<State>ONTARIO</State>
<Country>CANADA</Country>
</InvoiceTo>
<ShipTo>
<Name/>
<AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
<City/>
<PostCode/>
<State/>
<Country/>
</ShipTo>
<DetailLines>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 5309</PartNumber>
<PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
<UnitPrice>37.72</UnitPrice>
<LineTotal>37.72</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6420</PartNumber>
<PartDescription>PROVINCIAL DINNER FORK</PartDescription>
<UnitPrice>17.98</UnitPrice>
<LineTotal>107.88</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>72</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>JR20643</PartNumber>
<PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
<UnitPrice>.81</UnitPrice>
<LineTotal>58.32</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6410</PartNumber>
<PartDescription>PROVINCIAL TEASPOONS</PartDescription>
<UnitPrice>12.16</UnitPrice>
<LineTotal>72.96</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>0</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6411</PartNumber>
<PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
<QuantityBackOrdered>6</QuantityBackOrdered>
<UnitPrice>17.98</UnitPrice>
<LineTotal>0.00</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>DO 3218</PartNumber>
<PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
<UnitPrice>70.00</UnitPrice>
<DiscountPercentage>5.0</DiscountPercentage>
<LineTotal>66.50</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 195</PartNumber>
<PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
<UnitPrice>55.90</UnitPrice>
<LineTotal>55.90</LineTotal>
</DetailLine>
</DetailLines>
<Totals>
<SubTotal>399.28</SubTotal>
<DiscountTotal>3.50</DiscountTotal>
<FreightTotal>23.75</FreightTotal>
<GSTTotal>29.61</GSTTotal>
<ProvTaxTotal>33.84</ProvTaxTotal>
<OtherTotal>33.84</OtherTotal>
<InvoiceTotal>486.48</InvoiceTotal>
</Totals>
</Invoice>
</body>
</biztalk_1>
<?xml version="1.0" ?>
<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
<foo:header xmlns:foo="whomping-willow" plain="guido" quote='"' apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
<manifest>
<document>
<name>sample1</name>
<description>a simple invoice</description>
</document>
</manifest>
</foo:header>
<body>
<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
<Header>
<InvoiceNumber>01786</InvoiceNumber>
<InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
<OrderNo>55377</OrderNo>
<OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
<CustomerPO>GJ03405</CustomerPO>
<ShipMethod>DAVE 1</ShipMethod>
<ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
<CustomerID>K5211(34)</CustomerID>
<SalesPersonCode>23</SalesPersonCode>
<TaxID>23</TaxID>
</Header>
<InvoiceTo>
<Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
<AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
<AddressLine>WESTLAKE ACCESS</AddressLine>
<City>NORTH BAY</City>
<PostCode>L8B1O5</PostCode>
<State>ONTARIO</State>
<Country>CANADA</Country>
</InvoiceTo>
<ShipTo>
<Name/>
<AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
<City/>
<PostCode/>
<State/>
<Country/>
</ShipTo>
<DetailLines>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 5309</PartNumber>
<PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
<UnitPrice>37.72</UnitPrice>
<LineTotal>37.72</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6420</PartNumber>
<PartDescription>PROVINCIAL DINNER FORK</PartDescription>
<UnitPrice>17.98</UnitPrice>
<LineTotal>107.88</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>72</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>JR20643</PartNumber>
<PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
<UnitPrice>.81</UnitPrice>
<LineTotal>58.32</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6410</PartNumber>
<PartDescription>PROVINCIAL TEASPOONS</PartDescription>
<UnitPrice>12.16</UnitPrice>
<LineTotal>72.96</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>0</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6411</PartNumber>
<PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
<QuantityBackOrdered>6</QuantityBackOrdered>
<UnitPrice>17.98</UnitPrice>
<LineTotal>0.00</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>DO 3218</PartNumber>
<PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
<UnitPrice>70.00</UnitPrice>
<DiscountPercentage>5.0</DiscountPercentage>
<LineTotal>66.50</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 195</PartNumber>
<PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
<UnitPrice>55.90</UnitPrice>
<LineTotal>55.90</LineTotal>
</DetailLine>
</DetailLines>
<Totals>
<SubTotal>399.28</SubTotal>
<DiscountTotal>3.50</DiscountTotal>
<FreightTotal>23.75</FreightTotal>
<GSTTotal>29.61</GSTTotal>
<ProvTaxTotal>33.84</ProvTaxTotal>
<OtherTotal>33.84</OtherTotal>
<InvoiceTotal>486.48</InvoiceTotal>
</Totals>
</Invoice>
</body>
</biztalk_1>
<p xmlns:z="http://xml.zope.org/namespaces/tal">
<span z:define="local x str:hello brave new world">
<span z:content="text local:x">outer variable x, first appearance</span>
<span z:define="local x str:goodbye cruel world">
<span z:content="text local:x">inner variable x</span>
</span>
<span z:content="text local:x">outer variable x, second appearance</span>
</span>
</p>
<?xml version="1.0" ?>
<p xmlns:z="http://xml.zope.org/namespaces/tal">
<span z:define="local x str:hello brave new world">
<span z:content="text local:x">outer variable x, first appearance</span>
<span z:define="local x str:goodbye cruel world">
<span z:content="text local:x">inner variable x</span>
</span>
<span z:content="text local:x">outer variable x, second appearance</span>
</span>
</p>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal" xmlns:z="http://xml.zope.org/namespaces/tal" m:define-macro="body" z:define="global count python:0">
<ul m:define-macro="whoops">
<li z:repeat="item python:range(count)">
<span z:replace="item">1</span>
<span z:replace="global:message"/>
</li>
</ul>
<span z:define="global count python:2; global message str:hello world"/>
<p m:use-macro="whoops">use-macro
<span m:fill-slot="whoops">fill-slot</span>
</p>
<span z:define="global message str:goodbye cruel world"/>
<p m:use-macro="whoops">use-macro</p>
<p m:define-slot="whoops">define-slot</p>
</body>
</html>
<?xml version="1.0" ?>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal" xmlns:z="http://xml.zope.org/namespaces/tal" m:define-macro="body" z:define="global count python:0">
<ul m:define-macro="whoops">
<li z:repeat="item python:range(count)">
<span z:replace="item">1</span>
<span z:replace="global:message"/>
</li>
</ul>
<span z:define="global count python:2; global message str:hello world"/>
<p m:use-macro="whoops">use-macro
<span m:fill-slot="whoops">fill-slot</span>
</p>
<span z:define="global message str:goodbye cruel world"/>
<p m:use-macro="whoops">use-macro</p>
<p m:define-slot="whoops">define-slot</p>
</body>
</html>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="body">
<h1>This is the body of test5</h1>
</body>
</html>
<?xml version="1.0" ?>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="body">
<h1>This is the body of test5</h1>
</body>
</html>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal"
m:use-macro="tests/input/test05.html/body">
dummy body in test6
</body>
</html>
<?xml version="1.0" ?>
<html>
<body xmlns:m="http://xml.zope.org/namespaces/metal"
m:use-macro="tests/input/test05.xml/body">
dummy body in test6
</body>
</html>
<table xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="myTable">
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span m:define-slot="bottomRight">Bottom Right</span></td>
</tr>
</table>
<?xml version="1.0" ?>
<table xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="myTable">
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span m:define-slot="bottomRight">Bottom Right</span></td>
</tr>
</table>
<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.html/myTable">
<!-- macro use with slots -->
<tr>
<td>
<span m:fill-slot="bottomRight">
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
</span>
</td>
</tr>
</table>
<?xml version="1.0" ?>
<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.xml/myTable">
<!-- macro use with slots -->
<tr>
<td>
<span m:fill-slot="bottomRight">
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
</span>
</td>
</tr>
</table>
<html>
<body>
<p>
Just a bunch of text.
<p>more text...
<ul>
<li>first item
<li>second item
<ol>
<li>second list, first item
<li>second list, second item
<dl compact>
<dt>term 1
<dt>term 2
<dd>definition
</dl>
</ol>
<li>Now let's have a paragraph...
<p>My Paragraph
</li>
<li>And a table in a list item:
<table>
</table>
</ul>
</body>
</html>
<html>
<body>
<p>
Just a bunch of text.</p>
<p>more text...</p>
<ul>
<li>first item</li>
<li>second item
<ol>
<li>second list, first item</li>
<li>second list, second item
<dl compact="">
<dt>term 1</dt>
<dt>term 2</dt>
<dd>definition</dd>
</dl></li>
</ol></li>
<li>Now let's have a paragraph...
<p>My Paragraph</p>
</li>
<li>And a table in a list item:
<table>
</table></li>
</ul>
</body>
</html>
<html><body>
<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.html/myTable">
<!-- macro use with slots -->
<tr>
<td>
<span m:fill-slot="bottomRight">
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<hr>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<br><br>
</span>
</td>
</tr>
</table>
</body></html>
<html xmlns:tal="http://xml.zope.org/namespaces/tal">
<p tal:replace="structure string:&lt;a&gt;bar&lt;/a&gt;"
tal:attributes="href string:http://www.python.org">dummy text</p>
<p tal:define="x python:1" tal:on-error="string:bad boy!">
<span tal:define="x python:2">
<span tal:define="x python:3">
<span tal:content="python:1/0"/>
</span>
</span>
</p>
<p tal:on-error="string:x undefined">
<span tal:content="x"/>
</p>
</html>
<html xmlns:tal="http://xml.zope.org/namespaces/tal">
<p tal:replace="structure string:&lt;a&gt;bar&lt;/a&gt;"
tal:attributes="href string:http://www.python.org">dummy text</p>
<p tal:define="x python:1" tal:on-error="string:bad boy!">
<span tal:define="x python:2">
<span tal:define="x python:3">
<span tal:content="python:1/0"/>
</span>
</span>
</p>
<p tal:on-error="string:x undefined">
<span tal:content="x"/>
</p>
</html>
<span tal:define="global true python:1; global false python:0" />
<img ismap>
<img ismap=ismap>
<img ismap="ismap">
<img ismap="foo">
<img ismap tal:attributes="ismap true">
<img ismap tal:attributes="ismap false">
<img ismap tal:attributes="ismap nothing">
<img ismap tal:attributes="ismap default">
<img ismap="foo" tal:attributes="ismap true">
<img ismap="foo" tal:attributes="ismap false">
<img ismap="foo" tal:attributes="ismap nothing">
<img ismap="foo" tal:attributes="ismap default">
<img tal:attributes="ismap true">
<img tal:attributes="ismap false">
<img tal:attributes="ismap nothing">
<img tal:attributes="ismap default">
<span tal:define="global x string:x.gif" />
<img src="foo">
<img src="foo" tal:attributes="src x">
<img src="foo" tal:attributes="src nothing">
<img src="foo" tal:attributes="src default">
<img tal:attributes="src default">
Here's a stray greater than: >
<script>
<!-- no comment -->
<notag>
&noentity;
</script>
<table>
<tr>
<td tal:repeat="x python:['car', 'bike', 'broomstick']" tal:content="x">
</td>
</tr>
</table>
<p>
<span tal:repeat="x python:['Harry', 'Ron', 'Hermione']" tal:replace="x" />
</p>
<?xml version="1.0" ?>
<html xmlns:tal="http://xml.zope.org/namespaces/tal">
<table>
<tr>
<td tal:repeat="x python:['car', 'bike', 'broomstick']" tal:content="x">
</td>
</tr>
</table>
<p>
<span tal:repeat="x python:['Harry', 'Ron', 'Hermione']" tal:replace="x" />
</p>
</html>
<span metal:define-macro="INNER">
<span metal:define-slot="INNERSLOT">INNERSLOT</span>
</span>
<xxx metal:use-macro="INNER">
<xxx metal:fill-slot="INNERSLOT">inner-argument</xxx>
</xxx>
<div metal:define-macro="OUTER">
<div metal:use-macro="INNER">
<xxx metal:define-slot="OUTERSLOT" metal:fill-slot="INNERSLOT">
OUTERSLOT
</xxx>
</div>
</div>
<div metal:use-macro="OUTER">
<span>
<xxx>
<div metal:fill-slot="OUTERSLOT">outer-argument</div>
</xxx>
</span>
</div>
<div metal:use-macro="OUTER">
</div>
<a href="valid/link.html"
tal:attributes="href python:'/base/' + attrs['href']">blah, blah</a>
<input tal:attributes="value string:a &quot;laser&quot;" />
<?xml version="1.0"?>
<body xmlns:tal="http://xml.zope.org/namespaces/tal">
<ImG href="foo" Alt="bar"
tal:attributes="Href string:about:foo;alT string:baz" />
</body>
<tal:block tal:content="string:Yes">No</tal:block>
<tal:block content="string:Yes">No</tal:block>
<tal:block>Yes</tal:block>
<metal:block tal:content="string:Yes">No</metal:block>
<metal:block>Yes</metal:block>
<?xml version="1.0"?>
<body xmlns:z="http://xml.zope.org/namespaces/tal"
xmlns:z2="http://xml.zope.org/namespaces/metal">
<z:block z:content="string:Yes">No</z:block>
<z:block content="string:Yes">No</z:block>
<z:block>Yes</z:block>
<z2:block z:content="string:Yes">No</z2:block>
<z2:block>Yes</z2:block>
</body>
<p tal:omit-tag="">Content</p>
<p tal:omit-tag=""></p>
<img tal:omit-tag="">
<p tal:omit-tag="string:Yes">Content</p>
<p tal:omit-tag="string:Yes"></p>
<img tal:omit-tag="string:Yes">
<p tal:omit-tag="nothing">Content</p>
<p tal:omit-tag="nothing"></p>
<img tal:omit-tag="nothing">
<p tal:define="txt string:Yes" tal:omit-tag="" tal:content="txt">No</p>
<p tal:define="txt string:Yes" tal:omit-tag="" tal:replace="txt">No</p>
<p tal:omit-tag="" tal:content="default">Yes</p>
<p tal:omit-tag="" tal:replace="default">Yes</p>
<?xml version="1.0"?>
<body xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<p tal:omit-tag="">Content</p>
<p tal:omit-tag=""></p>
<img tal:omit-tag=""/>
<p tal:omit-tag="string:Yes">Content</p>
<p tal:omit-tag="string:Yes"></p>
<img tal:omit-tag="string:Yes"/>
<p tal:omit-tag="nothing">Content</p>
<p tal:omit-tag="nothing"></p>
<img tal:omit-tag="nothing" />
<p tal:define="txt string:Yes" tal:omit-tag="" tal:content="txt">No</p>
<p tal:define="txt string:Yes" tal:omit-tag="" tal:replace="txt">No</p>
<p tal:omit-tag="" tal:content="default">Yes</p>
<p tal:omit-tag="" tal:replace="default">Yes</p>
</body>
<input name="Delete"
tal:attributes="name string:delete_button"
i18n:attributes="name">
<input name="Delete"
i18n:attributes="name message-id">
<input i18n:attributes=" name message-id;
attr input-attr ">
<input name="Delete"
i18n:attributes="name message-id;">
<input name="Delete" title="titletext"
i18n:attributes="name; title">
<input name="Delete" tal:attributes="title string:titletaltext"
i18n:attributes="name; title">
<input name="Delete" tal:attributes="title string:titletaltext"
i18n:attributes="name message-id">
<input name="Delete" i18n:attributes="name">
<div i18n:translate="">At the tone the time will be
<span i18n:data="here/currentTime"
i18n:translate="timefmt"
i18n:name="time"
tal:omit-tag="">2:32 pm</span>... beep!</div>
<span i18n:translate="don't translate me">
stuff
<span tal:replace="string:foobar" i18n:name="longname" />
more stuff
</span>
<span i18n:translate="">
stuff
<span tal:replace="string:foobar" i18n:name="longname" />
more stuff
</span>
<span tal:replace="string:<foo>" />
<span i18n:translate="">
<span tal:replace="string:<foo>" i18n:name="name1" />
<span tal:replace="structure string:<bar />" i18n:name="name2" />
<span i18n:name="name3" tal:omit-tag=""><b>some</b> <i>text</i></span>
</span>
<input value="don't translate me"
i18n:attributes="value">
<span metal:define-macro="OUTER">
AAA
<span metal:define-macro="INNER">INNER</span>
BBB
</span>
<xxx metal:use-macro="OUTER">
</xxx>
<xxx metal:use-macro="INNER">
</xxx>
<span metal:define-macro="OUTER2">
AAA
<xxx metal:define-slot="OUTERSLOT">
<span metal:define-macro="INNER2">INNER</span>
</xxx>
BBB
</span>
<xxx metal:use-macro="OUTER2">
</xxx>
<xxx metal:use-macro="INNER2">
</xxx>
<xxx metal:use-macro="OUTER2">
<yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
</xxx>
<span metal:define-macro="OUTER3">
AAA
<xxx metal:define-slot="OUTERSLOT">
<span metal:define-macro="INNER3">INNER
<xxx metal:define-slot="INNERSLOT">INNERSLOT</xxx>
</span>
</xxx>
BBB
</span>
<xxx metal:use-macro="OUTER3">
</xxx>
<xxx metal:use-macro="OUTER3">
<yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
</xxx>
<xxx metal:use-macro="INNER3">
</xxx>
<xxx metal:use-macro="INNER3">
<yyy metal:fill-slot="INNERSLOT">INNERSLOT</yyy>
</xxx>
<xxx metal:use-macro="INNER3">
<yyy metal:fill-slot="INNERSLOT">
<zzz metal:define-macro="INSLOT">INSLOT</zzz>
</yyy>
</xxx>
<xxx metal:use-macro="INSLOT"></xxx>
<div metal:define-macro="OUTER">
OUTER
<span metal:define-macro="INNER">INNER</span>
OUTER
</div>
<div metal:use-macro="OUTER"/>
<span tal:attributes="class string:foo">Should not get attr in metal</span>
<!-- the outer element *must* be tal:something or metal:something -->
<metal:block define-macro="page" i18n:domain="zope">
<title metal:define-slot="title">Z3 UI</title>
</metal:block>
<!-- the outer element *must* include tal:omit-tag='' -->
<x tal:omit-tag="" metal:define-macro="page" i18n:domain="zope">
<title metal:define-slot="title">Z3 UI</title>
</x>
<metal:block define-macro="page">
<html i18:domain="zope">
<metal:block define-slot="title">Z3 UI</metal:block>
</html>
</metal:block>
<html metal:define-macro="page" i18n:domain="zope">
<x metal:define-slot="title" />
</html>
<html metal:use-macro="page" />
\ No newline at end of file
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html>
<head>dadada</head>
<body>
<h1>This
Is
The
Replaced
Title</h1>
<!-- test entity references -->
&nbsp;&HarryPotter;
<!-- examples adapted from TemplateAttributeLanguageSyntax -->
<span>here/id</span>
<p>5</p>
<p>
honda
</p>
<p>
subaru
</p>
<p>
acura
</p>
<p xml:foo="bar">foo bar</p>
<!-- more examples -->
<ul>
<span>
<li>honda</li>
</span>
<span>
<li>subaru</li>
</span>
<span>
<li>acura</li>
</span>
</ul>
<!-- test attribute expansion -->
<a href="http://python.org">python</a>
<a href="http://python.org">python</a>
<!-- test insert/replace structure -->
<span></span>
<span />
<span />
&nbsp;<h3>Header Level 3</h3>
<span>&nbsp;<h3>Header Level 3</h3></span>
</body>
</html>
<?xml version="1.0" ?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"DTD/xhtml1-transitional.dtd">
<html>
<head>dadada</head>
<body>
<h1>This Is The Replaced Title</h1>
<!-- test entity references -->
&nbsp;&HarryPotter;
<!-- examples adapted from TemplateAttributeLanguageSyntax -->
<span>here/id</span>
<p>5</p>
<p>
honda
</p>
<p>
subaru
</p>
<p>
acura
</p>
<p xml:foo="bar">foo bar</p>
<!-- more examples -->
<ul>
<span>
<li>honda</li>
</span>
<span>
<li>subaru</li>
</span>
<span>
<li>acura</li>
</span>
</ul>
<!-- test attribute expansion -->
<a href="http://python.org">python</a>
<a href="http://python.org">python</a>
<!-- test insert/replace structure -->
<span></span>
<span/>
<span/>
&nbsp;<h3>Header Level 3</h3>
<span>&nbsp;<h3>Header Level 3</h3></span>
</body>
</html>
<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
<foo:header xmlns:foo="whomping-willow" plain="guido" quote="&quot;" apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
<manifest>
<document>
<name>sample1</name>
<description>a simple invoice</description>
</document>
</manifest>
</foo:header>
<body>
<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
<invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
<header>
<invoicenumber>01786</invoicenumber>
<invoicedate>2000-03-17</invoicedate> <!-- March 17th, 2000 -->
<orderno>55377</orderno>
<orderdate>2000-03-15</orderdate> <!-- March 15th, 2000 -->
<customerpo>GJ03405</customerpo>
<shipmethod>DAVE 1</shipmethod>
<shipdate>2000-03-17</shipdate> <!-- March 17th, 2000 -->
<customerid>K5211(34)</customerid>
<salespersoncode>23</salespersoncode>
<taxid>23</taxid>
</header>
<invoiceto>
<name>SHIPWRIGHT RESTAURANTS LIMITED</name>
<addressline>125 NORTH SERVICE ROAD W</addressline>
<addressline>WESTLAKE ACCESS</addressline>
<city>NORTH BAY</city>
<postcode>L8B1O5</postcode>
<state>ONTARIO</state>
<country>CANADA</country>
</invoiceto>
<shipto>
<name />
<addressline>ATTN: PAULINE DEGRASSI</addressline>
<city />
<postcode />
<state />
<country />
</shipto>
<detaillines>
<detailline>
<quantityshipped>1</quantityshipped>
<unitofmeasure>CS</unitofmeasure>
<partnumber>DM 5309</partnumber>
<partdescription>#1013 12 OZ.MUNICH STEIN</partdescription>
<unitprice>37.72</unitprice>
<linetotal>37.72</linetotal>
</detailline>
<detailline>
<quantityshipped>6</quantityshipped>
<unitofmeasure>DZ</unitofmeasure>
<partnumber>ON 6420</partnumber>
<partdescription>PROVINCIAL DINNER FORK</partdescription>
<unitprice>17.98</unitprice>
<linetotal>107.88</linetotal>
</detailline>
<detailline>
<quantityshipped>72</quantityshipped>
<unitofmeasure>EA</unitofmeasure>
<partnumber>JR20643</partnumber>
<partdescription>PLASTIC HANDLED STEAK KNIFE</partdescription>
<unitprice>.81</unitprice>
<linetotal>58.32</linetotal>
</detailline>
<detailline>
<quantityshipped>6</quantityshipped>
<unitofmeasure>DZ</unitofmeasure>
<partnumber>ON 6410</partnumber>
<partdescription>PROVINCIAL TEASPOONS</partdescription>
<unitprice>12.16</unitprice>
<linetotal>72.96</linetotal>
</detailline>
<detailline>
<quantityshipped>0</quantityshipped>
<unitofmeasure>DZ</unitofmeasure>
<partnumber>ON 6411</partnumber>
<partdescription>PROVINCIAL RD BOWL SPOON</partdescription>
<quantitybackordered>6</quantitybackordered>
<unitprice>17.98</unitprice>
<linetotal>0.00</linetotal>
</detailline>
<detailline>
<quantityshipped>1</quantityshipped>
<unitofmeasure>EA</unitofmeasure>
<partnumber>DO 3218</partnumber>
<partdescription>34 OZ DUAL DIAL SCALE AM3218</partdescription>
<unitprice>70.00</unitprice>
<discountpercentage>5.0</discountpercentage>
<linetotal>66.50</linetotal>
</detailline>
<detailline>
<quantityshipped>1</quantityshipped>
<unitofmeasure>CS</unitofmeasure>
<partnumber>DM 195</partnumber>
<partdescription>20 OZ.BEER PUB GLASS</partdescription>
<unitprice>55.90</unitprice>
<linetotal>55.90</linetotal>
</detailline>
</detaillines>
<totals>
<subtotal>399.28</subtotal>
<discounttotal>3.50</discounttotal>
<freighttotal>23.75</freighttotal>
<gsttotal>29.61</gsttotal>
<provtaxtotal>33.84</provtaxtotal>
<othertotal>33.84</othertotal>
<invoicetotal>486.48</invoicetotal>
</totals>
</invoice>
</body>
</biztalk_1>
<?xml version="1.0" ?>
<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
<foo:header xmlns:foo="whomping-willow" plain="guido" quote="&quot;" apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
<manifest>
<document>
<name>sample1</name>
<description>a simple invoice</description>
</document>
</manifest>
</foo:header>
<body>
<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
<Header>
<InvoiceNumber>01786</InvoiceNumber>
<InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
<OrderNo>55377</OrderNo>
<OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
<CustomerPO>GJ03405</CustomerPO>
<ShipMethod>DAVE 1</ShipMethod>
<ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
<CustomerID>K5211(34)</CustomerID>
<SalesPersonCode>23</SalesPersonCode>
<TaxID>23</TaxID>
</Header>
<InvoiceTo>
<Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
<AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
<AddressLine>WESTLAKE ACCESS</AddressLine>
<City>NORTH BAY</City>
<PostCode>L8B1O5</PostCode>
<State>ONTARIO</State>
<Country>CANADA</Country>
</InvoiceTo>
<ShipTo>
<Name/>
<AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
<City/>
<PostCode/>
<State/>
<Country/>
</ShipTo>
<DetailLines>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 5309</PartNumber>
<PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
<UnitPrice>37.72</UnitPrice>
<LineTotal>37.72</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6420</PartNumber>
<PartDescription>PROVINCIAL DINNER FORK</PartDescription>
<UnitPrice>17.98</UnitPrice>
<LineTotal>107.88</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>72</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>JR20643</PartNumber>
<PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
<UnitPrice>.81</UnitPrice>
<LineTotal>58.32</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>6</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6410</PartNumber>
<PartDescription>PROVINCIAL TEASPOONS</PartDescription>
<UnitPrice>12.16</UnitPrice>
<LineTotal>72.96</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>0</QuantityShipped>
<UnitOfMeasure>DZ</UnitOfMeasure>
<PartNumber>ON 6411</PartNumber>
<PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
<QuantityBackOrdered>6</QuantityBackOrdered>
<UnitPrice>17.98</UnitPrice>
<LineTotal>0.00</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>EA</UnitOfMeasure>
<PartNumber>DO 3218</PartNumber>
<PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
<UnitPrice>70.00</UnitPrice>
<DiscountPercentage>5.0</DiscountPercentage>
<LineTotal>66.50</LineTotal>
</DetailLine>
<DetailLine>
<QuantityShipped>1</QuantityShipped>
<UnitOfMeasure>CS</UnitOfMeasure>
<PartNumber>DM 195</PartNumber>
<PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
<UnitPrice>55.90</UnitPrice>
<LineTotal>55.90</LineTotal>
</DetailLine>
</DetailLines>
<Totals>
<SubTotal>399.28</SubTotal>
<DiscountTotal>3.50</DiscountTotal>
<FreightTotal>23.75</FreightTotal>
<GSTTotal>29.61</GSTTotal>
<ProvTaxTotal>33.84</ProvTaxTotal>
<OtherTotal>33.84</OtherTotal>
<InvoiceTotal>486.48</InvoiceTotal>
</Totals>
</Invoice>
</body>
</biztalk_1>
<p>
<span>
<span>hello brave new world</span>
<span>
<span>goodbye cruel world</span>
</span>
<span>hello brave new world</span>
</span>
</p>
<?xml version="1.0" ?>
<p>
<span>
<span>hello brave new world</span>
<span>
<span>goodbye cruel world</span>
</span>
<span>hello brave new world</span>
</span>
</p>
<html>
<body>
<ul>
</ul>
<span />
<ul>
<li>
0
hello world
</li>
<li>
1
hello world
</li>
</ul>
<span />
<ul>
<li>
0
goodbye cruel world
</li>
<li>
1
goodbye cruel world
</li>
</ul>
<p>define-slot</p>
</body>
</html>
<?xml version="1.0" ?>
<html>
<body>
<ul>
</ul>
<span/>
<ul>
<li>
0
hello world
</li>
<li>
1
hello world
</li>
</ul>
<span/>
<ul>
<li>
0
goodbye cruel world
</li>
<li>
1
goodbye cruel world
</li>
</ul>
<p>define-slot</p>
</body>
</html>
<html>
<body>
<h1>This is the body of test5</h1>
</body>
</html>
<?xml version="1.0" ?>
<html>
<body>
<h1>This is the body of test5</h1>
</body>
</html>
<html>
<body>
<h1>This is the body of test5</h1>
</body>
</html>
<?xml version="1.0" ?>
<html>
<body>
<h1>This is the body of test5</h1>
</body>
</html>
<table>
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span>Bottom Right</span></td>
</tr>
</table>
<?xml version="1.0" ?>
<table>
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span>Bottom Right</span></td>
</tr>
</table>
<table>
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span>
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
</span></td>
</tr>
</table>
<?xml version="1.0" ?>
<table>
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span>
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
</span></td>
</tr>
</table>
<html>
<body>
<p>
Just a bunch of text.</p>
<p>more text...</p>
<ul>
<li>first item</li>
<li>second item
<ol>
<li>second list, first item</li>
<li>second list, second item
<dl compact>
<dt>term 1</dt>
<dt>term 2</dt>
<dd>definition</dd>
</dl></li>
</ol></li>
<li>Now let's have a paragraph...
<p>My Paragraph</p>
</li>
<li>And a table in a list item:
<table>
</table></li>
</ul>
</body>
</html>
<html>
<body>
<p>
Just a bunch of text.</p>
<p>more text...</p>
<ul>
<li>first item</li>
<li>second item
<ol>
<li>second list, first item</li>
<li>second list, second item
<dl compact="">
<dt>term 1</dt>
<dt>term 2</dt>
<dd>definition</dd>
</dl></li>
</ol></li>
<li>Now let's have a paragraph...
<p>My Paragraph</p>
</li>
<li>And a table in a list item:
<table>
</table></li>
</ul>
</body>
</html>
<html><body>
<table>
<!-- macro definition with slots -->
<tr>
<td>Top Left</td>
<td>Top Right</td>
</tr>
<tr>
<td>Bottom left</td>
<td><span>
<h1>Some headline</h1>
<p>This is the real contents of the bottom right slot.</p>
<hr>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<p>It is supposed to contain a lot of text. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
Blabber, blabber, blah. Baah, baah, barb.</p>
<br><br>
</span></td>
</tr>
</table>
</body></html>
<html>
<a href="http://www.python.org">bar</a>
<p>bad boy!</p>
<p>x undefined</p>
</html>
<html>
<a href="http://www.python.org">bar</a>
<p>bad boy!</p>
<p>x undefined</p>
</html>
<span />
<img ismap>
<img ismap="ismap">
<img ismap="ismap">
<img ismap="foo">
<img ismap="ismap">
<img>
<img>
<img ismap="ismap">
<img ismap="ismap">
<img>
<img>
<img ismap="foo">
<img ismap="ismap">
<img>
<img>
<img>
<span />
<img src="foo">
<img src="x.gif">
<img>
<img src="foo">
<img>
Here's a stray greater than: >
<script>
<!-- no comment -->
<notag>
&noentity;
</script>
<table>
<tr>
<td>car</td>
<td>bike</td>
<td>broomstick</td>
</tr>
</table>
<p>
Harry
Ron
Hermione
</p>
<?xml version="1.0" ?>
<html>
<table>
<tr>
<td>car</td>
<td>bike</td>
<td>broomstick</td>
</tr>
</table>
<p>
Harry
Ron
Hermione
</p>
</html>
<span>
<span>INNERSLOT</span>
</span>
<span>
<xxx>inner-argument</xxx>
</span>
<div>
<span>
<xxx>
OUTERSLOT
</xxx>
</span>
</div>
<div>
<span>
<div>outer-argument</div>
</span>
</div>
<div>
<span>
<xxx>
OUTERSLOT
</xxx>
</span>
</div>
<a href="/base/valid/link.html">blah, blah</a>
<input value="a &quot;laser&quot;" />
<?xml version="1.0"?>
<body>
<ImG href="foo" Alt="bar" alT="baz" Href="about:foo"/>
</body>
<?xml version="1.0"?>
<body>
Yes
Yes
Yes
Yes
Yes
</body>
Content
Content
<p>Content</p>
<p></p>
<img>
Yes
Yes
Yes
Yes
<?xml version="1.0"?>
<body>
Content
Content
<p>Content</p>
<p/>
<img/>
Yes
Yes
Yes
Yes
</body>
<input name="DELETE_BUTTON">
<input name="MESSAGE-ID">
<input name="MESSAGE-ID" attr="INPUT-ATTR">
<input name="MESSAGE-ID">
<input name="DELETE" title="TITLETEXT">
<input name="DELETE" title="TITLETALTEXT">
<input name="MESSAGE-ID" title="titletaltext">
<div>AT THE TONE THE TIME WILL BE 59 minutes after 6 PM... BEEP!</div>
<span>
stuff
foobar
more stuff
</span>
<span>STUFF foobar MORE STUFF</span>
&lt;foo&gt;
<span>&lt;foo&gt; <bar /> <b>some</b> <i>text</i></span>
<span metal:define-macro="OUTER">
AAA
<span metal:define-macro="INNER">INNER</span>
BBB
</span>
<span metal:use-macro="OUTER">
AAA
<span>INNER</span>
BBB
</span>
<span metal:use-macro="INNER">INNER</span>
<span metal:define-macro="OUTER2">
AAA
<xxx metal:define-slot="OUTERSLOT">
<span metal:define-macro="INNER2">INNER</span>
</xxx>
BBB
</span>
<span metal:use-macro="OUTER2">
AAA
<xxx>
<span>INNER</span>
</xxx>
BBB
</span>
<span metal:use-macro="INNER2">INNER</span>
<span metal:use-macro="OUTER2">
AAA
<yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
BBB
</span>
<span metal:define-macro="OUTER3">
AAA
<xxx metal:define-slot="OUTERSLOT">
<span metal:define-macro="INNER3">INNER
<xxx metal:define-slot="INNERSLOT">INNERSLOT</xxx>
</span>
</xxx>
BBB
</span>
<span metal:use-macro="OUTER3">
AAA
<xxx>
<span>INNER
<xxx>INNERSLOT</xxx>
</span>
</xxx>
BBB
</span>
<span metal:use-macro="OUTER3">
AAA
<yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
BBB
</span>
<span metal:use-macro="INNER3">INNER
<xxx>INNERSLOT</xxx>
</span>
<span metal:use-macro="INNER3">INNER
<yyy metal:fill-slot="INNERSLOT">INNERSLOT</yyy>
</span>
<span metal:use-macro="INNER3">INNER
<yyy metal:fill-slot="INNERSLOT">
<zzz metal:define-macro="INSLOT">INSLOT</zzz>
</yyy>
</span>
<zzz metal:use-macro="INSLOT">INSLOT</zzz>
<div metal:define-macro="OUTER">
OUTER
<span metal:define-macro="INNER">INNER</span>
OUTER
</div>
<div metal:use-macro="OUTER">
OUTER
<span>INNER</span>
OUTER
</div>
<span tal:attributes="class string:foo">Should not get attr in metal</span>
<!-- the outer element *must* be tal:something or metal:something -->
<metal:block define-macro="page" i18n:domain="zope">
<title metal:define-slot="title">Z3 UI</title>
</metal:block>
<!-- the outer element *must* include tal:omit-tag='' -->
<x tal:omit-tag="" metal:define-macro="page" i18n:domain="zope">
<title metal:define-slot="title">Z3 UI</title>
</x>
<metal:block define-macro="page">
<html i18:domain="zope">
<metal:block define-slot="title">Z3 UI</metal:block>
</html>
</metal:block>
<html metal:define-macro="page" i18n:domain="zope">
<x metal:define-slot="title" />
</html>
<html metal:use-macro="page" i18n:domain="zope">
<x />
</html>
#! /usr/bin/env python1.5
"""Run all tests."""
import sys
import utils
import unittest
import test_htmlparser
import test_htmltalparser
import test_talinterpreter
import test_files
import test_sourcepos
def test_suite():
suite = unittest.TestSuite()
suite.addTest(test_htmlparser.test_suite())
suite.addTest(test_htmltalparser.test_suite())
if not utils.skipxml:
import test_xmlparser
suite.addTest(test_xmlparser.test_suite())
suite.addTest(test_talinterpreter.test_suite())
suite.addTest(test_files.test_suite())
suite.addTest(test_sourcepos.test_suite())
return suite
def main():
return utils.run_suite(test_suite())
if __name__ == "__main__":
errs = main()
sys.exit(errs and 1 or 0)
#! /usr/bin/env python1.5
"""Tests that run driver.py over input files comparing to output files."""
import os
import sys
import glob
from TAL.tests import utils
import unittest
from zope.tal import runtest
class FileTestCase(unittest.TestCase):
def __init__(self, file, dir):
self.__file = file
self.__dir = dir
unittest.TestCase.__init__(self)
def shortDescription(self):
return os.path.join("...", "TAL", "tests", "input",
os.path.basename(self.__file))
def runTest(self):
basename = os.path.basename(self.__file)
#sys.stdout.write(basename + " ")
sys.stdout.flush()
if basename[:10] == 'test_metal':
sys.argv = ["", "-Q", "-m", self.__file]
else:
sys.argv = ["", "-Q", self.__file]
pwd = os.getcwd()
try:
try:
os.chdir(self.__dir)
runtest.main()
finally:
os.chdir(pwd)
except SystemExit, what:
if what.code:
self.fail("output for %s didn't match" % self.__file)
try:
script = __file__
except NameError:
script = sys.argv[0]
def test_suite():
suite = unittest.TestSuite()
dir = os.path.dirname(script)
dir = os.path.abspath(dir)
parentdir = os.path.dirname(dir)
prefix = os.path.join(dir, "input", "test*.")
if utils.skipxml:
xmlargs = []
else:
xmlargs = glob.glob(prefix + "xml")
xmlargs.sort()
htmlargs = glob.glob(prefix + "html")
htmlargs.sort()
args = xmlargs + htmlargs
if not args:
sys.stderr.write("Warning: no test input files found!!!\n")
for arg in args:
case = FileTestCase(arg, parentdir)
suite.addTest(case)
return suite
if __name__ == "__main__":
errs = utils.run_suite(test_suite())
sys.exit(errs and 1 or 0)
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for the HTMLTALParser code generator."""
import pprint
import sys
from TAL.tests import utils
import unittest
# BBB 2005/05/01 -- to be changed after 12 months
# ignore deprecation warnings on import for now
import warnings
showwarning = warnings.showwarning
warnings.showwarning = lambda *a, **k: None
# this old import should remain here until the TAL package is
# completely removed, so that API backward compatibility is properly
# tested
from TAL import HTMLTALParser
from TAL.TALDefs import TAL_VERSION, TALError, METALError
# restore warning machinery
warnings.showwarning = showwarning
class TestCaseBase(unittest.TestCase):
prologue = ""
epilogue = ""
initial_program = [('version', TAL_VERSION), ('mode', 'html')]
final_program = []
def _merge(self, p1, p2):
if p1 and p2:
op1, args1 = p1[-1]
op2, args2 = p2[0]
if op1.startswith('rawtext') and op2.startswith('rawtext'):
return (p1[:-1]
+ [rawtext(args1[0] + args2[0])]
+ p2[1:])
return p1+p2
def _run_check(self, source, program, macros={}):
parser = HTMLTALParser.HTMLTALParser()
parser.parseString(self.prologue + source + self.epilogue)
got_program, got_macros = parser.getCode()
program = self._merge(self.initial_program, program)
program = self._merge(program, self.final_program)
self.assert_(got_program == program,
"Program:\n" + pprint.pformat(got_program)
+ "\nExpected:\n" + pprint.pformat(program))
self.assert_(got_macros == macros,
"Macros:\n" + pprint.pformat(got_macros)
+ "\nExpected:\n" + pprint.pformat(macros))
def _get_check(self, source, program=[], macros={}):
parser = HTMLTALParser.HTMLTALParser()
parser.parseString(source)
got_program, got_macros = parser.getCode()
pprint.pprint(got_program)
pprint.pprint(got_macros)
def _should_error(self, source, exc=TALError):
def parse(self=self, source=source):
parser = HTMLTALParser.HTMLTALParser()
parser.parseString(self.prologue + source + self.epilogue)
self.assertRaises(exc, parse)
def rawtext(s):
"""Compile raw text to the appropriate instruction."""
if "\n" in s:
return ("rawtextColumn", (s, len(s) - (s.rfind("\n") + 1)))
else:
return ("rawtextOffset", (s, len(s)))
class HTMLTALParserTestCases(TestCaseBase):
def check_code_simple_identity(self):
self._run_check("""<html a='b' b="c" c=d><title>My Title</html>""", [
rawtext('<html a="b" b="c" c="d">'
'<title>My Title</title></html>'),
])
def check_code_implied_list_closings(self):
self._run_check("""<ul><li><p><p><li></ul>""", [
rawtext('<ul><li><p></p><p></p></li><li></li></ul>'),
])
self._run_check("""<dl><dt><dt><dd><dd><ol><li><li></ol></dl>""", [
rawtext('<dl><dt></dt><dt></dt><dd></dd>'
'<dd><ol><li></li><li></li></ol></dd></dl>'),
])
def check_code_implied_table_closings(self):
self._run_check("""<p>text <table><tr><th>head\t<tr><td>cell\t"""
"""<table><tr><td>cell \n \t \n<tr>""", [
rawtext('<p>text</p> <table><tr><th>head</th>'
'</tr>\t<tr><td>cell\t<table><tr><td>cell</td>'
'</tr> \n \t \n<tr></tr></table></td></tr></table>'),
])
self._run_check("""<table><tr><td>cell """
"""<table><tr><td>cell </table></table>""", [
rawtext('<table><tr><td>cell <table><tr><td>cell</td></tr>'
' </table></td></tr></table>'),
])
def check_code_bad_nesting(self):
def check(self=self):
self._run_check("<a><b></a></b>", [])
self.assertRaises(HTMLTALParser.NestingError, check)
def check_code_attr_syntax(self):
output = [
rawtext('<a b="v" c="v" d="v" e></a>'),
]
self._run_check("""<a b='v' c="v" d=v e>""", output)
self._run_check("""<a b = 'v' c = "v" d = v e>""", output)
self._run_check("""<a\nb\n=\n'v'\nc\n=\n"v"\nd\n=\nv\ne>""", output)
self._run_check("""<a\tb\t=\t'v'\tc\t=\t"v"\td\t=\tv\te>""", output)
def check_code_attr_values(self):
self._run_check(
"""<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'>""", [
rawtext('<a b="xxx\n\txxx" c="yyy\t\nyyy" d="\txyz\n"></a>')])
self._run_check("""<a b='' c="">""", [
rawtext('<a b="" c=""></a>'),
])
def check_code_attr_entity_replacement(self):
# we expect entities *not* to be replaced by HTLMParser!
self._run_check("""<a b='&amp;&gt;&lt;&quot;&apos;'>""", [
rawtext('<a b="&amp;&gt;&lt;&quot;\'"></a>'),
])
self._run_check("""<a b='\"'>""", [
rawtext('<a b="&quot;"></a>'),
])
self._run_check("""<a b='&'>""", [
rawtext('<a b="&amp;"></a>'),
])
self._run_check("""<a b='<'>""", [
rawtext('<a b="&lt;"></a>'),
])
def check_code_attr_funky_names(self):
self._run_check("""<a a.b='v' c:d=v e-f=v>""", [
rawtext('<a a.b="v" c:d="v" e-f="v"></a>'),
])
def check_code_pcdata_entityref(self):
self._run_check("""&nbsp;""", [
rawtext('&nbsp;'),
])
def check_code_short_endtags(self):
self._run_check("""<html><img/></html>""", [
rawtext('<html><img /></html>'),
])
class METALGeneratorTestCases(TestCaseBase):
def check_null(self):
self._run_check("", [])
def check_define_macro(self):
macro = self.initial_program + [
('startTag', ('p', [('metal:define-macro', 'M', 'metal')])),
rawtext('booh</p>'),
]
program = [
('setPosition', (1, 0)),
('defineMacro', ('M', macro)),
]
macros = {'M': macro}
self._run_check('<p metal:define-macro="M">booh</p>', program, macros)
def check_use_macro(self):
self._run_check('<p metal:use-macro="M">booh</p>', [
('setPosition', (1, 0)),
('useMacro',
('M', '$M$', {},
[('startTag', ('p', [('metal:use-macro', 'M', 'metal')])),
rawtext('booh</p>')])),
])
def check_define_slot(self):
macro = self.initial_program + [
('startTag', ('p', [('metal:define-macro', 'M', 'metal')])),
rawtext('foo'),
('setPosition', (1, 29)),
('defineSlot', ('S',
[('startTag', ('span', [('metal:define-slot', 'S', 'metal')])),
rawtext('spam</span>')])),
rawtext('bar</p>'),
]
program = [('setPosition', (1, 0)),
('defineMacro', ('M', macro))]
macros = {'M': macro}
self._run_check('<p metal:define-macro="M">foo'
'<span metal:define-slot="S">spam</span>bar</p>',
program, macros)
def check_fill_slot(self):
self._run_check('<p metal:use-macro="M">foo'
'<span metal:fill-slot="S">spam</span>bar</p>', [
('setPosition', (1, 0)),
('useMacro',
('M', '$M$',
{'S': [('startTag', ('span',
[('metal:fill-slot', 'S', 'metal')])),
rawtext('spam</span>')]},
[('startTag', ('p', [('metal:use-macro', 'M', 'metal')])),
rawtext('foo'),
('setPosition', (1, 26)),
('fillSlot', ('S',
[('startTag', ('span', [('metal:fill-slot', 'S', 'metal')])),
rawtext('spam</span>')])),
rawtext('bar</p>')])),
])
class TALGeneratorTestCases(TestCaseBase):
def check_null(self):
self._run_check("", [])
def check_define_1(self):
self._run_check("<p tal:define='xyzzy string:spam'></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:define': 'xyzzy string:spam'}),
('setLocal', ('xyzzy', '$string:spam$')),
('startTag', ('p', [('tal:define', 'xyzzy string:spam', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_define_2(self):
self._run_check("<p tal:define='local xyzzy string:spam'></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:define': 'local xyzzy string:spam'}),
('setLocal', ('xyzzy', '$string:spam$')),
('startTag', ('p',
[('tal:define', 'local xyzzy string:spam', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_define_3(self):
self._run_check("<p tal:define='global xyzzy string:spam'></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:define': 'global xyzzy string:spam'}),
('setGlobal', ('xyzzy', '$string:spam$')),
('startTag', ('p',
[('tal:define', 'global xyzzy string:spam', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_define_4(self):
self._run_check("<p tal:define='x string:spam; y x'></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:define': 'x string:spam; y x'}),
('setLocal', ('x', '$string:spam$')),
('setLocal', ('y', '$x$')),
('startTag', ('p', [('tal:define', 'x string:spam; y x', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_define_5(self):
self._run_check("<p tal:define='x string:;;;;; y x'></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:define': 'x string:;;;;; y x'}),
('setLocal', ('x', '$string:;;$')),
('setLocal', ('y', '$x$')),
('startTag', ('p', [('tal:define', 'x string:;;;;; y x', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_define_6(self):
self._run_check(
"<p tal:define='x string:spam; global y x; local z y'></p>", [
('setPosition', (1, 0)),
('beginScope',
{'tal:define': 'x string:spam; global y x; local z y'}),
('setLocal', ('x', '$string:spam$')),
('setGlobal', ('y', '$x$')),
('setLocal', ('z', '$y$')),
('startTag', ('p',
[('tal:define', 'x string:spam; global y x; local z y', 'tal')])),
('endScope', ()),
rawtext('</p>'),
])
def check_condition(self):
self._run_check(
"<p><span tal:condition='python:1'><b>foo</b></span></p>", [
rawtext('<p>'),
('setPosition', (1, 3)),
('beginScope', {'tal:condition': 'python:1'}),
('condition', ('$python:1$',
[('startTag', ('span', [('tal:condition', 'python:1', 'tal')])),
rawtext('<b>foo</b></span>')])),
('endScope', ()),
rawtext('</p>'),
])
def check_content_1(self):
self._run_check("<p tal:content='string:foo'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:content': 'string:foo'}),
('startTag', ('p', [('tal:content', 'string:foo', 'tal')])),
('insertText', ('$string:foo$', [rawtext('bar')])),
('endScope', ()),
rawtext('</p>'),
])
def check_content_2(self):
self._run_check("<p tal:content='text string:foo'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:content': 'text string:foo'}),
('startTag', ('p', [('tal:content', 'text string:foo', 'tal')])),
('insertText', ('$string:foo$', [rawtext('bar')])),
('endScope', ()),
rawtext('</p>'),
])
def check_content_3(self):
self._run_check("<p tal:content='structure string:<br>'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:content': 'structure string:<br>'}),
('startTag', ('p',
[('tal:content', 'structure string:<br>', 'tal')])),
('insertStructure',
('$string:<br>$', {}, [rawtext('bar')])),
('endScope', ()),
rawtext('</p>'),
])
def check_replace_1(self):
self._run_check("<p tal:replace='string:foo'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:replace': 'string:foo'}),
('optTag',
('p',
'',
None,
0,
[('startTag', ('p', [('tal:replace', 'string:foo', 'tal')]))],
[('insertText', ('$string:foo$', [rawtext('bar')]))])),
('endScope', ()),
])
def check_replace_2(self):
self._run_check("<p tal:replace='text string:foo'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:replace': 'text string:foo'}),
('optTag',
('p',
'',
None,
0,
[('startTag', ('p', [('tal:replace', 'text string:foo', 'tal')]))],
[('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])),
('endScope', ()),
])
def check_replace_3(self):
self._run_check("<p tal:replace='structure string:<br>'>bar</p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:replace': 'structure string:<br>'}),
('optTag',
('p',
'',
None,
0,
[('startTag', ('p', [('tal:replace', 'structure string:<br>', 'tal')]))],
[('insertStructure',
('$string:<br>$', {}, [('rawtextOffset', ('bar', 3))]))])),
('endScope', ()),
])
def check_repeat(self):
self._run_check("<p tal:repeat='x python:(1,2,3)'>"
"<span tal:replace='x'>dummy</span></p>", [
('setPosition', (1, 0)),
('beginScope', {'tal:repeat': 'x python:(1,2,3)'}),
('loop', ('x', '$python:(1,2,3)$',
[('startTag', ('p',
[('tal:repeat', 'x python:(1,2,3)', 'tal')])),
('setPosition', (1, 33)),
('beginScope', {'tal:replace': 'x'}),
('optTag',
('span',
'',
None,
0,
[('startTag', ('span', [('tal:replace', 'x', 'tal')]))],
[('insertText', ('$x$', [rawtext('dummy')]))])),
('endScope', ()),
rawtext('</p>')])),
('endScope', ()),
])
def check_attributes_1(self):
self._run_check("<a href='foo' name='bar' tal:attributes="
"'href string:http://www.zope.org; x string:y'>"
"link</a>", [
('setPosition', (1, 0)),
('beginScope',
{'tal:attributes': 'href string:http://www.zope.org; x string:y',
'name': 'bar', 'href': 'foo'}),
('startTag', ('a',
[('href', 'foo', 'replace', '$string:http://www.zope.org$', 0, None),
('name', 'name="bar"'),
('tal:attributes',
'href string:http://www.zope.org; x string:y', 'tal'),
('x', None, 'insert', '$string:y$', 0, None)])),
('endScope', ()),
rawtext('link</a>'),
])
def check_attributes_2(self):
self._run_check("<p tal:replace='structure string:<img>' "
"tal:attributes='src string:foo.png'>duh</p>", [
('setPosition', (1, 0)),
('beginScope',
{'tal:attributes': 'src string:foo.png',
'tal:replace': 'structure string:<img>'}),
('optTag',
('p',
'',
None,
0,
[('startTag',
('p',
[('tal:replace', 'structure string:<img>', 'tal'),
('tal:attributes', 'src string:foo.png', 'tal')]))],
[('insertStructure',
('$string:<img>$',
{'src': ('$string:foo.png$', False, None)},
[rawtext('duh')]))])),
('endScope', ()),
])
def check_on_error_1(self):
self._run_check("<p tal:on-error='string:error' "
"tal:content='notHere'>okay</p>", [
('setPosition', (1, 0)),
('beginScope',
{'tal:content': 'notHere', 'tal:on-error': 'string:error'}),
('onError',
([('startTag', ('p',
[('tal:on-error', 'string:error', 'tal'),
('tal:content', 'notHere', 'tal')])),
('insertText', ('$notHere$', [rawtext('okay')])),
rawtext('</p>')],
[('startTag', ('p',
[('tal:on-error', 'string:error', 'tal'),
('tal:content', 'notHere', 'tal')])),
('insertText', ('$string:error$', [])),
rawtext('</p>')])),
('endScope', ()),
])
def check_on_error_2(self):
self._run_check("<p tal:on-error='string:error' "
"tal:replace='notHere'>okay</p>", [
('setPosition', (1, 0)),
('beginScope',
{'tal:replace': 'notHere', 'tal:on-error': 'string:error'}),
('onError',
([('optTag',
('p',
'',
None,
0,
[('startTag',
('p',
[('tal:on-error', 'string:error', 'tal'),
('tal:replace', 'notHere', 'tal')]))],
[('insertText', ('$notHere$', [('rawtextOffset', ('okay', 4))]))]))],
[('startTag', ('p',
[('tal:on-error', 'string:error', 'tal'),
('tal:replace', 'notHere', 'tal')])),
('insertText', ('$string:error$', [])),
rawtext('</p>')])),
('endScope', ()),
])
def check_dup_attr(self):
self._should_error("<img tal:condition='x' tal:condition='x'>")
self._should_error("<img metal:define-macro='x' "
"metal:define-macro='x'>", METALError)
def check_tal_errors(self):
self._should_error("<p tal:define='x' />")
self._should_error("<p tal:repeat='x' />")
self._should_error("<p tal:foobar='x' />")
self._should_error("<p tal:replace='x' tal:content='x' />")
self._should_error("<p tal:replace='x'>")
for tag in HTMLTALParser.EMPTY_HTML_TAGS:
self._should_error("<%s tal:content='string:foo'>" % tag)
def check_metal_errors(self):
exc = METALError
self._should_error(2*"<p metal:define-macro='x'>xxx</p>", exc)
self._should_error("<html metal:use-macro='x'>" +
2*"<p metal:fill-slot='y' />" + "</html>", exc)
self._should_error("<p metal:foobar='x' />", exc)
self._should_error("<p metal:define-macro='x'>", exc)
#
# I18N test cases
#
def check_i18n_attributes(self):
self._run_check("<img alt='foo' i18n:attributes='alt'>", [
('setPosition', (1, 0)),
('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt'}),
('startTag', ('img',
[('alt', 'foo', 'replace', None, 1, None),
('i18n:attributes', 'alt', 'i18n')])),
('endScope', ()),
])
self._run_check("<img alt='foo' i18n:attributes='alt foo ; bar'>", [
('setPosition', (1, 0)),
('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt foo ; bar'}),
('startTag', ('img',
[('alt', 'foo', 'replace', None, 1, 'foo'),
('i18n:attributes', 'alt foo ; bar', 'i18n'),
('bar', None, 'insert', None, 1, None)])),
('endScope', ()),
])
self._should_error('''<input name="Delete"
tal:attributes="name string:delete_button"
i18n:attributes="name message-id">''')
def test_i18n_name_bad_name(self):
self._should_error("<span i18n:name='not a valid name' />")
self._should_error("<span i18n:name='-bad-name' />")
def test_i18n_attributes_repeated_attr(self):
self._should_error("<a i18n:attributes='href; href' />")
self._should_error("<a i18n:attributes='href; HREF' />")
def check_i18n_translate(self):
# input/test19.html
self._run_check('''\
<span i18n:translate="">Replace this</span>
<span i18n:translate="msgid">This is a
translated string</span>
<span i18n:translate="">And another
translated string</span>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation', ('', [('rawtextOffset', ('Replace this', 12))])),
('rawtextBeginScope',
('</span>\n', 0, (2, 0), 1, {'i18n:translate': 'msgid'})),
('startTag', ('span', [('i18n:translate', 'msgid', 'i18n')])),
('insertTranslation',
('msgid', [('rawtextColumn', ('This is a\ntranslated string', 17))])),
('rawtextBeginScope', ('</span>\n', 0, (4, 0), 1, {'i18n:translate': ''})),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('', [('rawtextColumn', ('And another\ntranslated string', 17))])),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))])
def check_i18n_translate_with_nested_tal(self):
self._run_check('''\
<span i18n:translate="">replaceable <p tal:replace="str:here">content</p></span>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextOffset', ('replaceable ', 12)),
('setPosition', (1, 36)),
('beginScope', {'tal:replace': 'str:here'}),
('optTag',
('p',
'',
None,
0,
[('startTag', ('p', [('tal:replace', 'str:here', 'tal')]))],
[('insertText',
('$str:here$', [rawtext('content')]))])),
('endScope', ())])),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))
])
def check_i18n_name(self):
# input/test21.html
self._run_check('''\
<span i18n:translate="">
<span tal:replace="str:Lomax" i18n:name="name" /> was born in
<span tal:replace="str:Antarctica" i18n:name="country" />.
</span>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextBeginScope',
('\n ',
2,
(2, 2),
0,
{'i18n:name': 'name', 'tal:replace': 'str:Lomax'})),
('i18nVariable',
('name',
[('optTag',
('span',
'',
None,
1,
[('startEndTag',
('span',
[('tal:replace', 'str:Lomax', 'tal'),
('i18n:name', 'name', 'i18n')]))],
[('insertText', ('$str:Lomax$', []))]))],
None,
False)),
('rawtextBeginScope',
(' was born in\n ',
2,
(3, 2),
1,
{'i18n:name': 'country', 'tal:replace': 'str:Antarctica'})),
('i18nVariable',
('country',
[('optTag',
('span',
'',
None,
1,
[('startEndTag',
('span',
[('tal:replace', 'str:Antarctica', 'tal'),
('i18n:name', 'country', 'i18n')]))],
[('insertText', ('$str:Antarctica$', []))]))],
None,
False)),
('endScope', ()),
('rawtextColumn', ('.\n', 0))])),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))
])
def test_i18n_name_with_content(self):
self._run_check('<div i18n:translate="">This is text for '
'<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>.'
'</div>', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('div', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextOffset', ('This is text for ', 17)),
('setPosition', (1, 40)),
('beginScope',
{'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
('i18nVariable',
('bar_name',
[('startTag',
('span',
[('i18n:translate', '', 'i18n'),
('tal:content', 'bar', 'tal'),
('i18n:name', 'bar_name', 'i18n')])),
('insertTranslation',
('',
[('insertText', ('$bar$', []))])),
('rawtextOffset', ('</span>', 7))],
None,
0)),
('endScope', ()),
('rawtextOffset', ('.', 1))])),
('endScope', ()),
('rawtextOffset', ('</div>', 6))
])
def check_i18n_name_implicit_value(self):
# input/test22.html
self._run_check('''\
<span i18n:translate="">
<span i18n:name="name"><b>Jim</b></span> was born in
<span i18n:name="country">the USA</span>.
</span>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('span', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextBeginScope', ('\n ', 2, (2, 2), 0, {'i18n:name': 'name'})),
('i18nVariable',
('name',
[('startTag', ('span', [('i18n:name', 'name', 'i18n')])),
('rawtextOffset', ('<b>Jim</b>', 10)),
('rawtextOffset', ('</span>', 7))],
None,
False)),
('rawtextBeginScope',
(' was born in\n ', 2, (3, 2), 1, {'i18n:name': 'country'})),
('i18nVariable',
('country',
[('startTag', ('span', [('i18n:name', 'country', 'i18n')])),
('rawtextOffset', ('the USA', 7)),
('rawtextOffset', ('</span>', 7))],
None,
False)),
('endScope', ()),
('rawtextColumn', ('.\n', 0))])),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))
])
def check_i18n_context_domain(self):
self._run_check("<span i18n:domain='mydomain'/>", [
('setPosition', (1, 0)),
('beginI18nContext', {'domain': 'mydomain',
'source': None, 'target': None}),
('beginScope', {'i18n:domain': 'mydomain'}),
('startEndTag', ('span', [('i18n:domain', 'mydomain', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def check_i18n_context_source(self):
self._run_check("<span i18n:source='en'/>", [
('setPosition', (1, 0)),
('beginI18nContext', {'source': 'en',
'domain': 'default', 'target': None}),
('beginScope', {'i18n:source': 'en'}),
('startEndTag', ('span', [('i18n:source', 'en', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def check_i18n_context_source_target(self):
self._run_check("<span i18n:source='en' i18n:target='ru'/>", [
('setPosition', (1, 0)),
('beginI18nContext', {'source': 'en', 'target': 'ru',
'domain': 'default'}),
('beginScope', {'i18n:source': 'en', 'i18n:target': 'ru'}),
('startEndTag', ('span', [('i18n:source', 'en', 'i18n'),
('i18n:target', 'ru', 'i18n')])),
('endScope', ()),
('endI18nContext', ()),
])
def check_i18n_context_in_define_slot(self):
text = ("<div metal:use-macro='M' i18n:domain='mydomain'>"
"<div metal:fill-slot='S'>spam</div>"
"</div>")
self._run_check(text, [
('setPosition', (1, 0)),
('useMacro',
('M', '$M$',
{'S': [('startTag', ('div',
[('metal:fill-slot', 'S', 'metal')])),
rawtext('spam</div>')]},
[('beginI18nContext', {'domain': 'mydomain',
'source': None, 'target': None}),
('beginScope',
{'i18n:domain': 'mydomain', 'metal:use-macro': 'M'}),
('startTag', ('div', [('metal:use-macro', 'M', 'metal'),
('i18n:domain', 'mydomain', 'i18n')])),
('setPosition', (1, 48)),
('fillSlot', ('S',
[('startTag',
('div', [('metal:fill-slot', 'S', 'metal')])),
rawtext('spam</div>')])),
('endScope', ()),
rawtext('</div>'),
('endI18nContext', ())])),
])
def check_i18n_data(self):
# input/test23.html
self._run_check('''\
<span i18n:data="here/currentTime"
i18n:translate="timefmt">2:32 pm</span>
''', [
('setPosition', (1, 0)),
('beginScope',
{'i18n:translate': 'timefmt', 'i18n:data': 'here/currentTime'}),
('startTag',
('span',
[('i18n:data', 'here/currentTime', 'i18n'),
('i18n:translate', 'timefmt', 'i18n')])),
('insertTranslation',
('timefmt', [('rawtextOffset', ('2:32 pm', 7))], '$here/currentTime$')),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))
])
def check_i18n_data_with_name(self):
# input/test29.html
self._run_check('''\
<div i18n:translate="">At the tone the time will be
<span i18n:data="here/currentTime"
i18n:translate="timefmt"
i18n:name="time">2:32 pm</span>... beep!</div>
''',
[('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('div', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextBeginScope',
('At the tone the time will be\n',
0,
(2, 0),
0,
{'i18n:data': 'here/currentTime',
'i18n:name': 'time',
'i18n:translate': 'timefmt'})),
('i18nVariable',
('time',
[('startTag',
('span',
[('i18n:data', 'here/currentTime', 'i18n'),
('i18n:translate', 'timefmt', 'i18n'),
('i18n:name', 'time', 'i18n')])),
('insertTranslation',
('timefmt',
[('rawtextOffset', ('2:32 pm', 7))],
'$here/currentTime$')),
('rawtextOffset', ('</span>', 7))],
None,
False)),
('endScope', ()),
('rawtextOffset', ('... beep!', 9))])),
('endScope', ()),
('rawtextColumn', ('</div>\n', 0))]
)
def check_i18n_explicit_msgid_with_name(self):
# input/test26.html
self._run_check('''\
<span i18n:translate="jobnum">
Job #<span tal:replace="context/@@object_name"
i18n:name="jobnum">NN</span></span>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': 'jobnum'}),
('startTag', ('span', [('i18n:translate', 'jobnum', 'i18n')])),
('insertTranslation',
('jobnum',
[('rawtextBeginScope',
('\n Job #',
9,
(2, 9),
0,
{'i18n:name': 'jobnum', 'tal:replace': 'context/@@object_name'})),
('i18nVariable',
('jobnum',
[('optTag',
('span',
'',
None,
0,
[('startTag',
('span',
[('tal:replace', 'context/@@object_name', 'tal'),
('i18n:name', 'jobnum', 'i18n')]))],
[('insertText',
('$context/@@object_name$',
[('rawtextOffset', ('NN', 2))]))]))],
None,
False)),
('endScope', ())])),
('endScope', ()),
('rawtextColumn', ('</span>\n', 0))
])
def check_i18n_name_around_tal_content(self):
# input/test28.html
self._run_check('''\
<p i18n:translate="verify">Your contact email address is recorded as
<span i18n:name="email">
<a href="mailto:user@example.com"
tal:content="request/submitter">user@host.com</a></span>
</p>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': 'verify'}),
('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])),
('insertTranslation',
('verify',
[('rawtextBeginScope',
('Your contact email address is recorded as\n ',
4,
(2, 4),
0,
{'i18n:name': 'email'})),
('i18nVariable',
('email',
[('startTag', ('span', [('i18n:name', 'email', 'i18n')])),
('rawtextBeginScope',
('\n ',
4,
(3, 4),
0,
{'href': 'mailto:user@example.com',
'tal:content': 'request/submitter'})),
('startTag',
('a',
[('href', 'href="mailto:user@example.com"'),
('tal:content', 'request/submitter', 'tal')])),
('insertText',
('$request/submitter$',
[('rawtextOffset', ('user@host.com', 13))])),
('endScope', ()),
('rawtextOffset', ('</a>', 4)),
('rawtextOffset', ('</span>', 7))],
None,
False)),
('endScope', ()),
('rawtextColumn', ('\n', 0))])),
('endScope', ()),
('rawtextColumn', ('</p>\n', 0))])
def check_i18n_name_with_tal_content(self):
# input/test27.html
self._run_check('''\
<p i18n:translate="verify">Your contact email address is recorded as
<a href="mailto:user@example.com"
tal:content="request/submitter"
i18n:name="email">user@host.com</a>
</p>
''', [
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': 'verify'}),
('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])),
('insertTranslation',
('verify',
[('rawtextBeginScope',
('Your contact email address is recorded as\n ',
4,
(2, 4),
0,
{'href': 'mailto:user@example.com',
'i18n:name': 'email',
'tal:content': 'request/submitter'})),
('i18nVariable',
('email',
[('startTag',
('a',
[('href', 'href="mailto:user@example.com"'),
('tal:content', 'request/submitter', 'tal'),
('i18n:name', 'email', 'i18n')])),
('insertText',
('$request/submitter$',
[('rawtextOffset', ('user@host.com', 13))])),
('rawtextOffset', ('</a>', 4))],
None,
0)),
('endScope', ()),
('rawtextColumn', ('\n', 0))])),
('endScope', ()),
('rawtextColumn', ('</p>\n', 0))
])
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(HTMLTALParserTestCases, "check_"))
suite.addTest(unittest.makeSuite(METALGeneratorTestCases, "check_"))
suite.addTest(unittest.makeSuite(TALGeneratorTestCases, "check_"))
return suite
if __name__ == "__main__":
errs = utils.run_suite(test_suite())
sys.exit(errs and 1 or 0)
#! /usr/bin/env python
"""Tests for TALInterpreter."""
import sys
import unittest
from StringIO import StringIO
# BBB 2005/05/01 -- to be changed after 12 months
# ignore deprecation warnings on import for now
import warnings
showwarning = warnings.showwarning
warnings.showwarning = lambda *a, **k: None
# this old import should remain here until the TAL package is
# completely removed, so that API backward compatibility is properly
# tested
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALInterpreter import TALInterpreter
from TAL.TALGenerator import TALGenerator
from TAL.DummyEngine import DummyEngine
# restore warning machinery
warnings.showwarning = showwarning
page1 = '''<html metal:use-macro="main"><body>
<div metal:fill-slot="body">
page1=<span tal:replace="position:" />
</div>
</body></html>'''
main_template = '''<html metal:define-macro="main"><body>
main_template=<span tal:replace="position:" />
<div metal:define-slot="body" />
main_template=<span tal:replace="position:" />
<div metal:use-macro="foot" />
main_template=<span tal:replace="position:" />
</body></html>'''
footer = '''<div metal:define-macro="foot">
footer=<span tal:replace="position:" />
</div>'''
expected = '''<html><body>
main_template=main_template (2,14)
<div>
page1=page1 (3,6)
</div>
main_template=main_template (4,14)
<div>
footer=footer (2,7)
</div>
main_template=main_template (6,14)
</body></html>'''
class Tests(unittest.TestCase):
def parse(self, eng, s, fn):
gen = TALGenerator(expressionCompiler=eng, xml=0, source_file=fn)
parser = HTMLTALParser(gen)
parser.parseString(s)
program, macros = parser.getCode()
return program, macros
def testSourcePositions(self):
"""Ensure source file and position are set correctly by TAL"""
macros = {}
eng = DummyEngine(macros)
page1_program, page1_macros = self.parse(eng, page1, 'page1')
main_template_program, main_template_macros = self.parse(
eng, main_template, 'main_template')
footer_program, footer_macros = self.parse(eng, footer, 'footer')
macros['main'] = main_template_macros['main']
macros['foot'] = footer_macros['foot']
stream = StringIO()
interp = TALInterpreter(page1_program, macros, eng, stream)
interp()
self.assertEqual(stream.getvalue().strip(), expected.strip(),
stream.getvalue())
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(Tests))
return suite
if __name__ == "__main__":
unittest.main()
# -*- coding: ISO-8859-1 -*-
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for TALInterpreter.
$Id$
"""
import sys
import unittest
from StringIO import StringIO
# BBB 2005/05/01 -- to be changed after 12 months
# ignore deprecation warnings on import for now
import warnings
showwarning = warnings.showwarning
warnings.showwarning = lambda *a, **k: None
# this old import should remain here until the TAL package is
# completely removed, so that API backward compatibility is properly
# tested
from TAL.TALDefs import METALError, I18NError
from TAL.HTMLTALParser import HTMLTALParser
from TAL.TALParser import TALParser
from TAL.TALInterpreter import TALInterpreter
from TAL.DummyEngine import DummyEngine, DummyTranslationService
from TAL.TALInterpreter import interpolate
# restore warning machinery
warnings.showwarning = showwarning
from TAL.tests import utils
from zope.i18nmessageid import Message
class TestCaseBase(unittest.TestCase):
def _compile(self, source):
parser = HTMLTALParser()
parser.parseString(source)
program, macros = parser.getCode()
return program, macros
class MacroErrorsTestCase(TestCaseBase):
def setUp(self):
dummy, macros = self._compile('<p metal:define-macro="M">Booh</p>')
self.macro = macros['M']
self.engine = DummyEngine(macros)
program, dummy = self._compile('<p metal:use-macro="M">Bah</p>')
self.interpreter = TALInterpreter(program, {}, self.engine)
def tearDown(self):
try:
self.interpreter()
except METALError:
pass
else:
self.fail("Expected METALError")
def test_mode_error(self):
self.macro[1] = ("mode", "duh")
def test_version_error(self):
self.macro[0] = ("version", "duh")
class I18NCornerTestCase(TestCaseBase):
def setUp(self):
self.engine = DummyEngine()
self.engine.setLocal('foo', Message('FoOvAlUe', 'default'))
self.engine.setLocal('bar', 'BaRvAlUe')
self.engine.setLocal('raw', ' \tRaW\n ')
self.engine.setLocal('noxlt', Message("don't translate me"))
def _check(self, program, expected):
result = StringIO()
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
self.assertEqual(expected, result.getvalue())
def test_simple_messageid_translate(self):
# This test is mainly here to make sure our DummyEngine works
# correctly.
program, macros = self._compile('<span tal:content="foo"/>')
self._check(program, '<span>FOOVALUE</span>\n')
program, macros = self._compile('<span tal:replace="foo"/>')
self._check(program, 'FOOVALUE\n')
def test_replace_with_messageid_and_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<span tal:replace="foo" i18n:name="foo_name"/>'
'</div>')
self._check(program, '<div>FOOVALUE</div>\n')
def test_pythonexpr_replace_with_messageid_and_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<span tal:replace="python: foo" i18n:name="foo_name"/>'
'</div>')
self._check(program, '<div>FOOVALUE</div>\n')
def test_structure_replace_with_messageid_and_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<span tal:replace="structure foo" i18n:name="foo_name"'
' i18n:translate=""/>'
'</div>')
self._check(program, '<div>FOOVALUE</div>\n')
def test_complex_replace_with_messageid_and_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<em i18n:name="foo_name" tal:omit-tag="">'
'<span tal:replace="foo"/>'
'</em>'
'</div>')
self._check(program, '<div>FOOVALUE</div>\n')
def test_content_with_messageid_and_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<span tal:content="foo" i18n:name="foo_name"/>'
'</div>')
self._check(program, '<div><span>FOOVALUE</span></div>\n')
def test_content_with_messageid_and_i18nname_and_i18ntranslate(self):
# Let's tell the user this is incredibly silly!
self.assertRaises(
I18NError, self._compile,
'<span i18n:translate="" tal:content="foo" i18n:name="foo_name"/>')
def test_content_with_plaintext_and_i18nname_and_i18ntranslate(self):
# Let's tell the user this is incredibly silly!
self.assertRaises(
I18NError, self._compile,
'<span i18n:translate="" i18n:name="color_name">green</span>')
def test_translate_static_text_as_dynamic(self):
program, macros = self._compile(
'<div i18n:translate="">This is text for '
'<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>.'
'</div>')
self._check(program,
'<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n')
def test_translate_static_text_as_dynamic_from_bytecode(self):
program = [('version', '1.6'),
('mode', 'html'),
('setPosition', (1, 0)),
('beginScope', {'i18n:translate': ''}),
('startTag', ('div', [('i18n:translate', '', 'i18n')])),
('insertTranslation',
('',
[('rawtextOffset', ('This is text for ', 17)),
('setPosition', (1, 40)),
('beginScope',
{'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
('i18nVariable',
('bar_name',
[('startTag',
('span',
[('i18n:translate', '', 'i18n'),
('tal:content', 'bar', 'tal'),
('i18n:name', 'bar_name', 'i18n')])),
('insertTranslation',
('',
[('insertText', ('$bar$', []))])),
('rawtextOffset', ('</span>', 7))],
None,
0)),
('endScope', ()),
('rawtextOffset', ('.', 1))])),
('endScope', ()),
('rawtextOffset', ('</div>', 6))
]
self._check(program,
'<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n')
def test_for_correct_msgids(self):
self.engine.translationDomain.clearMsgids()
result = StringIO()
program, macros = self._compile(
'<div i18n:translate="">This is text for '
'<span i18n:translate="" tal:content="bar" '
'i18n:name="bar_name"/>.</div>')
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
msgids = self.engine.translationDomain.getMsgids('default')
msgids.sort()
self.assertEqual(2, len(msgids))
self.assertEqual('BaRvAlUe', msgids[0][0])
self.assertEqual('This is text for ${bar_name}.', msgids[1][0])
self.assertEqual({'bar_name': '<span>BARVALUE</span>'}, msgids[1][1])
self.assertEqual(
'<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n',
result.getvalue())
def test_for_raw_msgids(self):
# Test for Issue 314: i18n:translate removes line breaks from
# <pre>...</pre> contents
# HTML mode
self.engine.translationDomain.clearMsgids()
result = StringIO()
program, macros = self._compile(
'<div i18n:translate=""> This is text\n'
' \tfor\n div. </div>'
'<pre i18n:translate=""> This is text\n'
' <b>\tfor</b>\n pre. </pre>')
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
msgids = self.engine.translationDomain.getMsgids('default')
msgids.sort()
self.assertEqual(2, len(msgids))
self.assertEqual(' This is text\n <b>\tfor</b>\n pre. ', msgids[0][0])
self.assertEqual('This is text for div.', msgids[1][0])
self.assertEqual(
'<div>THIS IS TEXT FOR DIV.</div>'
'<pre> THIS IS TEXT\n <B>\tFOR</B>\n PRE. </pre>\n',
result.getvalue())
# XML mode
self.engine.translationDomain.clearMsgids()
result = StringIO()
parser = TALParser()
parser.parseString(
'<?xml version="1.0"?>\n'
'<pre xmlns:i18n="http://xml.zope.org/namespaces/i18n"'
' i18n:translate=""> This is text\n'
' <b>\tfor</b>\n barvalue. </pre>')
program, macros = parser.getCode()
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
msgids = self.engine.translationDomain.getMsgids('default')
msgids.sort()
self.assertEqual(1, len(msgids))
self.assertEqual('This is text <b> for</b> barvalue.', msgids[0][0])
self.assertEqual(
'<?xml version="1.0"?>\n'
'<pre>THIS IS TEXT <B> FOR</B> BARVALUE.</pre>\n',
result.getvalue())
def test_raw_msgids_and_i18ntranslate_i18nname(self):
self.engine.translationDomain.clearMsgids()
result = StringIO()
program, macros = self._compile(
'<div i18n:translate=""> This is text\n \tfor\n'
'<pre tal:content="raw" i18n:name="raw"'
' i18n:translate=""></pre>.</div>')
self.interpreter = TALInterpreter(program, {}, self.engine,
stream=result)
self.interpreter()
msgids = self.engine.translationDomain.getMsgids('default')
msgids.sort()
self.assertEqual(2, len(msgids))
self.assertEqual(' \tRaW\n ', msgids[0][0])
self.assertEqual('This is text for ${raw}.', msgids[1][0])
self.assertEqual({'raw': '<pre> \tRAW\n </pre>'}, msgids[1][1])
self.assertEqual(
u'<div>THIS IS TEXT FOR <pre> \tRAW\n </pre>.</div>\n',
result.getvalue())
def test_for_handling_unicode_vars(self):
# Make sure that non-ASCII Unicode is substituted correctly.
# http://collector.zope.org/Zope3-dev/264
program, macros = self._compile(
"<div i18n:translate='' tal:define='bar python:unichr(0xC0)'>"
"Foo <span tal:replace='bar' i18n:name='bar' /></div>")
self._check(program, u"<div>FOO \u00C0</div>\n")
def test_for_untranslated_messageid_simple(self):
program, macros = self._compile('<span tal:content="noxlt"/>')
self._check(program, "<span>don't translate me</span>\n")
def test_for_untranslated_messageid_i18nname(self):
program, macros = self._compile(
'<div i18n:translate="" >'
'<span tal:replace="python: noxlt" i18n:name="foo_name"/>'
'</div>')
self._check(program, "<div>don't translate me</div>\n")
class I18NErrorsTestCase(TestCaseBase):
def _check(self, src, msg):
try:
self._compile(src)
except I18NError:
pass
else:
self.fail(msg)
def test_id_with_replace(self):
self._check('<p i18n:id="foo" tal:replace="string:splat"></p>',
"expected i18n:id with tal:replace to be denied")
def test_missing_values(self):
self._check('<p i18n:attributes=""></p>',
"missing i18n:attributes value not caught")
self._check('<p i18n:data=""></p>',
"missing i18n:data value not caught")
self._check('<p i18n:id=""></p>',
"missing i18n:id value not caught")
def test_id_with_attributes(self):
self._check('''<input name="Delete"
tal:attributes="name string:delete_button"
i18n:attributes="name message-id">''',
"expected attribute being both part of tal:attributes" +
" and having a msgid in i18n:attributes to be denied")
class OutputPresentationTestCase(TestCaseBase):
def test_attribute_wrapping(self):
# To make sure the attribute-wrapping code is invoked, we have to
# include at least one TAL/METAL attribute to avoid having the start
# tag optimized into a rawtext instruction.
INPUT = r"""
<html this='element' has='a' lot='of' attributes=', so' the='output'
needs='to' be='line' wrapped='.' tal:define='foo nothing'>
</html>"""
EXPECTED = r'''
<html this="element" has="a" lot="of"
attributes=", so" the="output" needs="to"
be="line" wrapped=".">
</html>''' "\n"
self.compare(INPUT, EXPECTED)
def test_unicode_content(self):
INPUT = """<p tal:content="python:u'dj-vu'">para</p>"""
EXPECTED = u"""<p>dj-vu</p>""" "\n"
self.compare(INPUT, EXPECTED)
def test_unicode_structure(self):
INPUT = """<p tal:replace="structure python:u'dj-vu'">para</p>"""
EXPECTED = u"""dj-vu""" "\n"
self.compare(INPUT, EXPECTED)
def test_i18n_replace_number(self):
INPUT = """
<p i18n:translate="foo ${bar}">
<span tal:replace="python:123" i18n:name="bar">para</span>
</p>"""
EXPECTED = u"""
<p>FOO 123</p>""" "\n"
self.compare(INPUT, EXPECTED)
def test_entities(self):
INPUT = ('<img tal:define="foo nothing" '
'alt="&a; &#1; &#x0a; &a &#45 &; &#0a; <>" />')
EXPECTED = ('<img alt="&a; &#1; &#x0a; '
'&amp;a &amp;#45 &amp;; &amp;#0a; &lt;&gt;" />\n')
self.compare(INPUT, EXPECTED)
def compare(self, INPUT, EXPECTED):
program, macros = self._compile(INPUT)
sio = StringIO()
interp = TALInterpreter(program, {}, DummyEngine(), sio, wrap=60)
interp()
self.assertEqual(sio.getvalue(), EXPECTED)
class InterpolateTestCase(TestCaseBase):
def test_syntax_ok(self):
text = "foo ${bar_0MAN} $baz_zz bee"
mapping = {'bar_0MAN': 'fish', 'baz_zz': 'moo'}
expected = "foo fish moo bee"
self.assertEqual(interpolate(text, mapping), expected)
def test_syntax_bad(self):
text = "foo $_bar_man} $ ${baz bee"
mapping = {'_bar_man': 'fish', 'baz': 'moo'}
expected = text
self.assertEqual(interpolate(text, mapping), expected)
def test_missing(self):
text = "foo ${bar} ${baz}"
mapping = {'bar': 'fish'}
expected = "foo fish ${baz}"
self.assertEqual(interpolate(text, mapping), expected)
def test_redundant(self):
text = "foo ${bar}"
mapping = {'bar': 'fish', 'baz': 'moo'}
expected = "foo fish"
self.assertEqual(interpolate(text, mapping), expected)
def test_numeric(self):
text = "foo ${bar}"
mapping = {'bar': 123}
expected = "foo 123"
self.assertEqual(interpolate(text, mapping), expected)
def test_unicode(self):
text = u"foo ${bar}"
mapping = {u'bar': u'baz'}
expected = u"foo baz"
self.assertEqual(interpolate(text, mapping), expected)
# this test just tests sick behaviour, we'll disable it
#def test_unicode_mixed_unknown_encoding(self):
# # This test assumes that sys.getdefaultencoding is ascii...
# text = u"foo ${bar}"
# mapping = {u'bar': 'd\xe9j\xe0'}
# expected = u"foo d\\xe9j\\xe0"
# self.assertEqual(interpolate(text, mapping), expected)
def test_suite():
suite = unittest.makeSuite(I18NErrorsTestCase)
suite.addTest(unittest.makeSuite(MacroErrorsTestCase))
suite.addTest(unittest.makeSuite(OutputPresentationTestCase))
suite.addTest(unittest.makeSuite(I18NCornerTestCase))
suite.addTest(unittest.makeSuite(InterpolateTestCase))
return suite
if __name__ == "__main__":
errs = utils.run_suite(test_suite())
sys.exit(errs and 1 or 0)
#! /usr/bin/env python1.5
"""Tests for XMLParser.py."""
import string
import sys
from TAL.tests import utils
import unittest
# BBB 2005/05/01 -- to be changed after 12 months
# ignore deprecation warnings on import for now
import warnings
showwarning = warnings.showwarning
warnings.showwarning = lambda *a, **k: None
# this old import should remain here until the TAL package is
# completely removed, so that API backward compatibility is properly
# tested
from TAL import XMLParser
# restore warning machinery
warnings.showwarning = showwarning
class EventCollector(XMLParser.XMLParser):
def __init__(self):
self.events = []
self.append = self.events.append
XMLParser.XMLParser.__init__(self)
self.parser.ordered_attributes = 1
def get_events(self):
# Normalize the list of events so that buffer artefacts don't
# separate runs of contiguous characters.
L = []
prevtype = None
for event in self.events:
type = event[0]
if type == prevtype == "data":
L[-1] = ("data", L[-1][1] + event[1])
else:
L.append(event)
prevtype = type
self.events = L
return L
# structure markup
def StartElementHandler(self, tag, attrs):
self.append(("starttag", tag, attrs))
def EndElementHandler(self, tag):
self.append(("endtag", tag))
# all other markup
def CommentHandler(self, data):
self.append(("comment", data))
def handle_charref(self, data):
self.append(("charref", data))
def CharacterDataHandler(self, data):
self.append(("data", data))
def StartDoctypeDeclHandler(self, rootelem, publicId, systemId, subset):
self.append(("doctype", rootelem, systemId, publicId, subset))
def XmlDeclHandler(self, version, encoding, standalone):
self.append(("decl", version, encoding, standalone))
def ExternalEntityRefHandler(self, data):
self.append(("entityref", data))
def ProcessingInstructionHandler(self, target, data):
self.append(("pi", target, data))
class EventCollectorExtra(EventCollector):
def handle_starttag(self, tag, attrs):
EventCollector.handle_starttag(self, tag, attrs)
self.append(("starttag_text", self.get_starttag_text()))
class SegmentedFile:
def __init__(self, parts):
self.parts = list(parts)
def read(self, bytes):
if self.parts:
s = self.parts.pop(0)
else:
s = ''
return s
class XMLParserTestCase(unittest.TestCase):
def _run_check(self, source, events, collector=EventCollector):
parser = collector()
if isinstance(source, type([])):
parser.parseStream(SegmentedFile(source))
else:
parser.parseString(source)
if utils.oldexpat:
while events[0][0] in ('decl', 'doctype'):
del events[0]
self.assertEquals(parser.get_events(), events)
def _run_check_extra(self, source, events):
self._run_check(source, events, EventCollectorExtra)
def _parse_error(self, source):
def parse(source=source):
parser = XMLParser.XMLParser()
parser.parseString(source)
self.assertRaises(XMLParser.XMLParseError, parse)
def check_processing_instruction_plus(self):
self._run_check("<?processing instruction?><a/>", [
("pi", "processing", "instruction"),
("starttag", "a", []),
("endtag", "a"),
])
def _check_simple_html(self):
self._run_check("""\
<?xml version='1.0' encoding='iso-8859-1'?>
<!DOCTYPE html PUBLIC 'foo' 'bar'>
<html>&entity;&#32;
<!--comment1a
-></foo><bar>&lt;<?pi?></foo<bar
comment1b-->
<img src='Bar' ismap=''/>sample
text
<!--comment2a- -comment2b-->
</html>
""", [
("decl", "1.0", "iso-8859-1", -1),
("doctype", "html", "foo", "bar", 0),
("starttag", "html", []),
# ("entityref", "entity"),
("data", " \n"),
("comment", "comment1a\n-></foo><bar>&lt;<?pi?></foo<bar\ncomment1b"),
("data", "\n"),
("starttag", "img", ["src", "Bar", "ismap", ""]),
("endtag", "img"),
("data", "sample\ntext\n"),
("comment", "comment2a- -comment2b"),
("data", "\n"),
("endtag", "html"),
])
def check_bad_nesting(self):
try:
self._run_check("<a><b></a></b>", [
("starttag", "a", []),
("starttag", "b", []),
("endtag", "a"),
("endtag", "b"),
])
except:
e = sys.exc_info()[1]
self.assert_(e.lineno == 1,
"did not receive correct position information")
else:
self.fail("expected parse error: bad nesting")
def check_attr_syntax(self):
output = [
("starttag", "a", ["b", "v", "c", "v"]),
("endtag", "a"),
]
self._run_check("""<a b='v' c="v"/>""", output)
self._run_check("""<a b = 'v' c = "v"/>""", output)
self._run_check("""<a\nb\n=\n'v'\nc\n=\n"v"\n/>""", output)
self._run_check("""<a\tb\t=\t'v'\tc\t=\t"v"\t/>""", output)
def check_attr_values(self):
self._run_check("""<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'/>""",
[("starttag", "a", ["b", "xxx xxx",
"c", "yyy yyy",
"d", " xyz "]),
("endtag", "a"),
])
self._run_check("""<a b='' c="" d=''/>""", [
("starttag", "a", ["b", "", "c", "", "d", ""]),
("endtag", "a"),
])
def check_attr_entity_replacement(self):
self._run_check("""<a b='&amp;&gt;&lt;&quot;&apos;'/>""", [
("starttag", "a", ["b", "&><\"'"]),
("endtag", "a"),
])
def check_attr_funky_names(self):
self._run_check("""<a a.b='v' e-f='v'/>""", [
("starttag", "a", ["a.b", "v", "e-f", "v"]),
("endtag", "a"),
])
def check_starttag_end_boundary(self):
self._run_check("""<a b='&lt;'/>""", [
("starttag", "a", ["b", "<"]),
("endtag", "a"),
])
self._run_check("""<a b='&gt;'/>""", [
("starttag", "a", ["b", ">"]),
("endtag", "a"),
])
def check_buffer_artefacts(self):
output = [("starttag", "a", ["b", "<"]), ("endtag", "a")]
self._run_check(["<a b='&lt;'/>"], output)
self._run_check(["<a ", "b='&lt;'/>"], output)
self._run_check(["<a b", "='&lt;'/>"], output)
self._run_check(["<a b=", "'&lt;'/>"], output)
self._run_check(["<a b='&lt;", "'/>"], output)
self._run_check(["<a b='&lt;'", "/>"], output)
output = [("starttag", "a", ["b", ">"]), ("endtag", "a")]
self._run_check(["<a b='&gt;'/>"], output)
self._run_check(["<a ", "b='&gt;'/>"], output)
self._run_check(["<a b", "='&gt;'/>"], output)
self._run_check(["<a b=", "'&gt;'/>"], output)
self._run_check(["<a b='&gt;", "'/>"], output)
self._run_check(["<a b='&gt;'", "/>"], output)
def check_starttag_junk_chars(self):
self._parse_error("<")
self._parse_error("<>")
self._parse_error("</>")
self._parse_error("</$>")
self._parse_error("</")
self._parse_error("</a")
self._parse_error("</a")
self._parse_error("<a<a>")
self._parse_error("</a<a>")
self._parse_error("<$")
self._parse_error("<$>")
self._parse_error("<!")
self._parse_error("<a $>")
self._parse_error("<a")
self._parse_error("<a foo='bar'")
self._parse_error("<a foo='bar")
self._parse_error("<a foo='>'")
self._parse_error("<a foo='>")
def check_declaration_junk_chars(self):
self._parse_error("<!DOCTYPE foo $ >")
# Support for the Zope regression test framework:
def test_suite(skipxml=utils.skipxml):
suite = unittest.TestSuite()
if not skipxml:
suite.addTest(unittest.makeSuite(XMLParserTestCase, "check_"))
return suite
if __name__ == "__main__":
errs = utils.run_suite(test_suite(skipxml=0))
sys.exit(errs and 1 or 0)
"""Helper functions for the test suite."""
import os
import sys
mydir = os.path.abspath(os.path.dirname(__file__))
codedir = os.path.dirname(os.path.dirname(mydir))
if codedir not in sys.path:
sys.path.append(codedir)
import unittest
import xml.parsers.expat
# Set skipxml to true if an XML parser could not be found.
# (But Python always includes one now.)
skipxml = False
# Set oldexpat if the StartDoctypeDeclHandler and XmlDeclHandler are
# not supported. The tests need to know whether the events reported
# by those handlers should be expected, but need to make sure the
# right thing is returned if they are.
#
oldexpat = False
p = xml.parsers.expat.ParserCreate()
#
# Can't use hasattr() since pyexpat supports the handler
# attributes in a broken way.
try:
p.StartDoctypeDeclHandler = None
except AttributeError:
oldexpat = True
def run_suite(suite, outf=None, errf=None):
if outf is None:
outf = sys.stdout
runner = unittest.TextTestRunner(outf)
result = runner.run(suite)
## print "\n\n"
## if result.errors:
## print "Errors (unexpected exceptions):"
## map(print_error, result.errors)
## print
## if result.failures:
## print "Failures (assertion failures):"
## map(print_error, result.failures)
## print
newerrs = len(result.errors) + len(result.failures)
if newerrs:
print "'Errors' indicate exceptions other than AssertionError."
print "'Failures' indicate AssertionError"
if errf is None:
errf = sys.stderr
errf.write("%d errors, %d failures\n"
% (len(result.errors), len(result.failures)))
return newerrs
def print_error(info):
testcase, (type, e, tb) = info
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Helper program to time compilation and interpretation
BBB 2005/05/01 -- to be removed after 12 months
"""
import zope.deprecation
zope.deprecation.moved('zope.tal.timer', '2.12')
if __name__ == "__main__":
main()
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Iterator class
BBB 2005/05/01 -- to be removed after 12 months
$Id$
"""
import zope.deprecation
zope.deprecation.deprecated(
'Iterator',
'Iterator has been deprecated and will be removed in Zope 2.12. '
'PageTemplates now use the Zope 3 implementation. Use ZopeIterator '
'from Products.PageTemplates.Expressions instead.'
)
class Iterator:
'''Simple Iterator class'''
__allow_access_to_unprotected_subobjects__ = 1
nextIndex = 0
def __init__(self, seq):
self.seq = seq
if iterInner._supports(seq, self):
self._inner = iterInner
self._prep_next = iterInner.prep_next
return
raise TypeError, "Iterator does not support %s" % `seq`
def __getattr__(self, name):
try:
inner = getattr(self._inner, 'it_' + name)
except AttributeError:
raise AttributeError, name
return inner(self)
def next(self):
if not (hasattr(self, '_next') or self._prep_next(self)):
return 0
self.index = i = self.nextIndex
self.nextIndex = i+1
self._advance(self)
return 1
def _advance(self, it):
self.item = self._next
del self._next
del self.end
self._advance = self._inner.advance
self.start = 1
def number(self): return self.nextIndex
def even(self): return not self.index % 2
def odd(self): return self.index % 2
def parity(self):
if self.index % 2:
return 'odd'
return 'even'
def letter(self, base=ord('a'), radix=26):
index = self.index
s = ''
while 1:
index, off = divmod(index, radix)
s = chr(base + off) + s
if not index: return s
def Letter(self):
return self.letter(base=ord('A'))
def Roman(self, rnvalues=(
(1000,'M'),(900,'CM'),(500,'D'),(400,'CD'),
(100,'C'),(90,'XC'),(50,'L'),(40,'XL'),
(10,'X'),(9,'IX'),(5,'V'),(4,'IV'),(1,'I')) ):
n = self.index + 1
s = ''
for v, r in rnvalues:
rct, n = divmod(n, v)
s = s + r * rct
return s
def roman(self):
return self.Roman().lower()
def first(self, name=None):
if self.start: return 1
return not self.same_part(name, self._last, self.item)
def last(self, name=None):
if self.end: return 1
return not self.same_part(name, self.item, self._next)
def length(self):
return len(self.seq)
def same_part(self, name, ob1, ob2):
if name is None:
return ob1 == ob2
no = []
return getattr(ob1, name, no) == getattr(ob2, name, no) is not no
def __iter__(self):
return IterIter(self)
class InnerBase:
'''Base Inner class for Iterators'''
# Prep sets up ._next and .end
def prep_next(self, it):
it.next = self.no_next
it.end = 1
return 0
# Advance knocks them down
def advance(self, it):
it._last = it.item
it.item = it._next
del it._next
del it.end
it.start = 0
def no_next(self, it):
return 0
def it_end(self, it):
if hasattr(it, '_next'):
return 0
return not self.prep_next(it)
class IterInner(InnerBase):
'''Iterator inner class for objects with Python iterators'''
def _supports(self, ob, it):
try:
it.iter = iter(ob)
return 1
except:
return 0
def prep_next(self, it):
try:
it._next = it.iter.next()
except StopIteration:
it._prep_next = self.no_next
it.end = 1
return 0
it.end = 0
return 1
class IterIter:
def __init__(self, it):
self.it = it
self.skip = it.nextIndex > 0 and not it.end
def next(self):
it = self.it
if self.skip:
self.skip = 0
return it.item
if it.next():
return it.item
raise StopIteration
iterInner = IterInner()
from __future__ import generators
import os, sys, unittest
import zope.deprecation
zope.deprecation.__show__.off()
from ZTUtils import Iterator
zope.deprecation.__show__.on()
try:
iter
do_piter_test = 1
except NameError:
do_piter_test = 0
class itemIterator:
'Ignore the __getitem__ argument in order to catch non-monotonic access.'
def __init__(self, n):
self.n = n
self.i = 0
def __getitem__(self, i):
if self.i >= self.n:
raise IndexError
i = self.i
self.i = self.i + 1
return i
class genIterator:
'Generator-based iteration'
def __init__(self, n):
self.n = n
def __iter__(self):
for i in range(self.n):
yield i
class IteratorTests(unittest.TestCase):
def testIterator0(self):
it = Iterator(())
assert not it.next(), "Empty iterator"
def testIterator1(self):
it = Iterator((1,))
assert it.next() and not it.next(), "Single-element iterator"
def testIteratorMany(self):
it = Iterator('text')
for c in 'text':
assert it.next(), "Multi-element iterator"
assert not it.next(), "Multi-element iterator"
def testStart(self):
for size in range(4):
it = Iterator(range(size+1))
it.next()
assert it.start, "Start true on element 1 of %s" % (size + 1)
el = 1
while it.next():
el = el + 1
assert not it.start, (
"Start false on element %s of %s" % (el, size+1))
def testEnd(self):
for size in range(4):
size = size + 1
it = Iterator(range(size))
el = 0
while it.next():
el = el + 1
if el == size:
assert it.end, "End true on element %s" % size
else:
assert not it.end, (
"End false on element %s of %s" % (el, size))
def assertRangeMatch(self, ob, n):
it = Iterator(ob)
for el in range(n):
assert it.next(), "Iterator stopped too soon"
assert it.index == el, "Incorrect index"
assert it.number() == el + 1, "Incorrect number"
assert it.item == el, "Incorrect item"
def testIndex(self):
self.assertRangeMatch(range(5), 5)
self.assertRangeMatch((0,1,2,3,4), 5)
self.assertRangeMatch({0:0, 1:1, 2:2, 3:3, 4:4}, 5)
self.assertRangeMatch(itemIterator(5), 5)
self.assertRangeMatch(genIterator(5), 5)
def testFirstLast(self):
it = Iterator([1])
it.next()
assert it.first() == it.last() == 1, "Bad first/last on singleton"
four = range(4)
for a in 2,3:
for b in four:
for c in four:
s = 'a' * a + 'b' * b + 'c' * c
it = Iterator(s)
it.next()
assert it.first(), "First element not first()"
last = s[0]
lastlast = it.last()
while it.next():
assert ((it.item != last) == it.first()), (
"first() error")
assert ((it.item != last) == lastlast), (
"last() error" % (it.item,
last, lastlast))
last = it.item
lastlast = it.last()
assert lastlast, "Last element not last()"
if do_piter_test:
def testIterOfIter(self):
for i in range(4):
r = range(i)
it1 = Iterator(r)
it2 = Iterator(iter(r))
while it1.next() and it2.next():
assert it1.item == it2.item, "Item mismatch with iter()"
assert it1.index == it2.index, (
"Index mismatch with iter()")
assert not (it1.next() or it2.next()), (
"Length mismatch with iter()")
def testIterIter(self):
wo_iter = map(lambda x:(x, x), range(4))
for i in range(4):
r = range(i)
w_iter = []
it = Iterator(r)
for x in it:
w_iter.append((x, it.index))
assert w_iter == wo_iter[:i], (
"for-loop failure on full iterator")
it = Iterator(range(4))
it.next(); it.next(); it.next()
w_iter = []
for x in it:
w_iter.append((x, it.index))
assert w_iter == wo_iter[2:], "for-loop failure on half iteration"
def test_suite():
return unittest.makeSuite(IteratorTests)
if __name__=='__main__':
main()
......@@ -535,30 +535,14 @@ class Resource(ExtensionClass.Base, Lockable.LockableItem):
ob = self._getCopy(parent)
ob._setId(name)
try:
orig_container._delObject(orig_id, suppress_events=True)
except TypeError:
# BBB: removed in Zope 2.11
orig_container._delObject(orig_id)
warnings.warn(
"%s._delObject without suppress_events is deprecated "
"and will be removed in Zope 2.11." %
orig_container.__class__.__name__, DeprecationWarning)
if existing:
object=getattr(parent, name)
self.dav__validate(object, 'DELETE', REQUEST)
parent._delObject(name)
try:
parent._setObject(name, ob, set_owner=0, suppress_events=True)
except TypeError:
# BBB: removed in Zope 2.11
parent._setObject(name, ob, set_owner=0)
warnings.warn(
"%s._setObject without suppress_events is deprecated "
"and will be removed in Zope 2.11." %
parent.__class__.__name__, DeprecationWarning)
ob = parent._getOb(name)
notify(ObjectMovedEvent(ob, orig_container, orig_id, parent, name))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment