Commit cfe8ff8d authored by Evan Simpson's avatar Evan Simpson

Add the tal:omit_tag statement.

Make elements obey the XML rule: attributes without explicit namespaces are in the same namespace as the tag. As a corollary, TAL output with markup removed also removes tags that are in the TAL and METAL namespace.
parent 3f1c0646
......@@ -104,7 +104,7 @@ class DummyEngine:
if macros is None:
macros = {}
self.macros = macros
dict = {'nothing': None}
dict = {'nothing': None, 'default': Default}
self.locals = self.globals = dict
self.stack = [dict]
......@@ -170,7 +170,7 @@ class DummyEngine:
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is not None:
if text is not None and text is not Default:
text = str(text)
return text
......
......@@ -192,9 +192,9 @@ class HTMLTALParser(HTMLParser):
def handle_starttag(self, tag, attrs):
self.close_para_tags(tag)
self.tagstack.append(tag)
self.scan_xmlns(attrs)
attrlist, taldict, metaldict = self.extract_attrs(attrs)
tag, attrlist, taldict, metaldict = self.process_ns(tag, attrs)
self.tagstack.append(tag)
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos())
if tag in EMPTY_HTML_TAGS:
......@@ -203,7 +203,7 @@ class HTMLTALParser(HTMLParser):
def handle_startendtag(self, tag, attrs):
self.close_para_tags(tag)
self.scan_xmlns(attrs)
attrlist, taldict, metaldict = self.extract_attrs(attrs)
tag, attrlist, taldict, metaldict = self.process_ns(tag, attrs)
if taldict.get("content"):
self.gen.emitStartElement(tag, attrlist, taldict, metaldict,
self.getpos())
......@@ -304,29 +304,43 @@ class HTMLTALParser(HTMLParser):
def pop_xmlns(self):
self.nsdict = self.nsstack.pop()
def extract_attrs(self, attrs):
def fixname(self, name):
if ':' in name:
prefix, suffix = string.split(name, ':', 1)
if prefix == 'xmlns':
nsuri = self.nsdict.get(suffix)
if nsuri in (ZOPE_TAL_NS, ZOPE_METAL_NS):
return name, name, prefix
else:
nsuri = self.nsdict.get(prefix)
if nsuri == ZOPE_TAL_NS:
return name, suffix, 'tal'
elif nsuri == ZOPE_METAL_NS:
return name, suffix, 'metal'
return name, name, 0
def process_ns(self, name, attrs):
attrlist = []
taldict = {}
metaldict = {}
name, namebase, namens = self.fixname(name)
for item in attrs:
key, value = item
if ':' in key:
prefix, suffix = string.split(key, ':', 1)
nsuri = self.nsdict.get(prefix)
if nsuri == ZOPE_METAL_NS:
if metaldict.has_key(suffix):
raise METALError("duplicate METAL attribute " +
`suffix`, self.getpos())
item = (key, value, "metal")
metaldict[suffix] = value
elif nsuri == ZOPE_TAL_NS:
if taldict.has_key(suffix):
raise TALError("duplicate TAL attribute " +
`suffix`, self.getpos())
item = (key, value, "tal")
taldict[suffix] = value
elif (prefix == "xmlns" and
value in (ZOPE_METAL_NS, ZOPE_TAL_NS)):
item = (key, value, "xmlns")
key, keybase, keyns = self.fixname(key)
ns = keyns or namens # default to tag namespace
if ns and ns != 'unknown':
item = (key, value, ns)
if ns == 'tal':
if taldict.has_key(keybase):
raise TALError("duplicate TAL attribute " +
`keybase`, self.getpos())
taldict[keybase] = value
elif ns == 'metal':
if metaldict.has_key(keybase):
raise METALError("duplicate METAL attribute " +
`keybase`, self.getpos())
metaldict[keybase] = value
attrlist.append(item)
return attrlist, taldict, metaldict
if namens in ('metal', 'tal'):
taldict['tal tag'] = namens
return name, attrlist, taldict, metaldict
......@@ -113,6 +113,8 @@ KNOWN_TAL_ATTRIBUTES = [
"repeat",
"attributes",
"on-error",
"omit-tag",
"tal tag",
]
class TALError(Exception):
......
......@@ -263,6 +263,18 @@ class TALGenerator:
else:
self.emit("endTag", name)
def emitOptTag(self, name, optTag, isend):
program = self.popProgram() #block
start = self.popProgram() #start tag
if (isend or not program) and self.xml:
# Minimize empty element
start[-1] = ("startEndTag",) + start[-1][1:]
isend = 1
cexpr = optTag[0]
if cexpr:
cexpr = self.compileExpression(optTag[0])
self.emit("optTag", name, cexpr, optTag[1], isend, start, program)
def emitRawText(self, text):
self.emit("rawtext", text)
......@@ -434,6 +446,8 @@ class TALGenerator:
replace = taldict.get("replace")
attrsubst = taldict.get("attributes")
onError = taldict.get("on-error")
omitTag = taldict.get("omit-tag")
TALtag = taldict.get("tal tag")
if len(metaldict) > 1 and (defineMacro or useMacro):
raise METALError("define-macro and use-macro cannot be used "
"together or with define-slot or fill-slot",
......@@ -492,6 +506,10 @@ class TALGenerator:
if replace:
todo["replace"] = replace
self.pushProgram()
optTag = omitTag is not None or TALtag
if optTag:
todo["optional tag"] = omitTag, TALtag
self.pushProgram()
if attrsubst:
repldict = parseAttributeReplacements(attrsubst)
for key, value in repldict.items():
......@@ -502,6 +520,8 @@ class TALGenerator:
todo["repldict"] = repldict
repldict = {}
self.emitStartTag(name, self.replaceAttrs(attrlist, repldict), isend)
if optTag:
self.pushProgram()
if content:
self.pushProgram()
if todo and position != (None, None):
......@@ -531,6 +551,7 @@ class TALGenerator:
define = todo.get("define")
repldict = todo.get("repldict", {})
scope = todo.get("scope")
optTag = todo.get("optional tag")
if implied > 0:
if defineMacro or useMacro or defineSlot or fillSlot:
......@@ -544,7 +565,9 @@ class TALGenerator:
if content:
self.emitSubstitution(content, {}, position)
if not isend:
if optTag:
self.emitOptTag(name, optTag, isend)
elif not isend:
self.emitEndTag(name)
if replace:
self.emitSubstitution(replace, repldict, position)
......
......@@ -416,6 +416,35 @@ class TALInterpreter:
return ok, name, value
bytecode_handlers["<attrAction>"] = attrAction
def no_tag(self, start, program):
state = self.saveState()
self.stream = stream = StringIO()
self._stream_write = stream.write
self.interpret(start)
self.restoreOutputState(state)
self.interpret(program)
def do_optTag(self, (name, cexpr, tag_ns, isend, start, program),
omit=0):
if tag_ns and not self.showtal:
return self.no_tag(start, program)
self.interpret(start)
if not isend:
self.interpret(program)
s = '</%s>' % name
self._stream_write(s)
self.col = self.col + len(s)
def do_optTag_tal(self, stuff):
cexpr = stuff[1]
if cexpr is not None and (cexpr == '' or
self.engine.evaluateBoolean(cexpr)):
self.no_tag(stuff[-2], stuff[-1])
else:
self.do_optTag(stuff)
bytecode_handlers["optTag"] = do_optTag
def dumpMacroStack(self, prefix, suffix, value):
sys.stderr.write("+---- %s%s = %s\n" % (prefix, suffix, value))
for i in range(len(self.macroStack)):
......@@ -649,6 +678,7 @@ class TALInterpreter:
bytecode_handlers_tal["loop"] = do_loop_tal
bytecode_handlers_tal["onError"] = do_onError_tal
bytecode_handlers_tal["<attrAction>"] = attrAction_tal
bytecode_handlers_tal["optTag"] = do_optTag_tal
def test():
......
......@@ -116,7 +116,6 @@ class TALParser(XMLParser):
self.nsDict = self.nsStack.pop()
def StartElementHandler(self, name, attrs):
name = self.fixname(name)
if self.ordered_attributes:
# attrs is a list of alternating names and values
attrlist = []
......@@ -128,28 +127,29 @@ class TALParser(XMLParser):
# attrs is a dict of {name: value}
attrlist = attrs.items()
attrlist.sort() # For definiteness
attrlist, taldict, metaldict = self.extractattrs(attrlist)
name, attrlist, taldict, metaldict = self.process_ns(name, attrlist)
attrlist = self.xmlnsattrs() + attrlist
self.gen.emitStartElement(name, attrlist, taldict, metaldict)
def extractattrs(self, attrlist):
talprefix = ZOPE_TAL_NS + " "
ntal = len(talprefix)
metalprefix = ZOPE_METAL_NS + " "
nmetal = len(metalprefix)
def process_ns(self, name, attrlist):
taldict = {}
metaldict = {}
fixedattrlist = []
name, namebase, namens = self.fixname(name)
for key, value in attrlist:
item = self.fixname(key), value
if key[:nmetal] == metalprefix:
metaldict[key[nmetal:]] = value
key, keybase, keyns = self.fixname(key)
ns = keyns or namens # default to tag namespace
item = key, value
if ns == 'metal':
metaldict[keybase] = value
item = item + ("metal",)
elif key[:ntal] == talprefix:
taldict[key[ntal:]] = value
elif ns == 'tal':
taldict[keybase] = value
item = item + ("tal",)
fixedattrlist.append(item)
return fixedattrlist, taldict, metaldict
if namens in ('metal', 'tal'):
taldict['tal tag'] = namens
return name, fixedattrlist, taldict, metaldict
def xmlnsattrs(self):
newlist = []
......@@ -170,12 +170,19 @@ class TALParser(XMLParser):
if ' ' in name:
uri, name = string.split(name, ' ')
prefix = self.nsDict[uri]
prefixed = name
if prefix:
name = "%s:%s" % (prefix, name)
return name
prefixed = "%s:%s" % (prefix, name)
ns = 'x'
if uri == ZOPE_TAL_NS:
ns = 'tal'
elif uri == ZOPE_METAL_NS:
ns = 'metal'
return (prefixed, name, ns)
return (name, name, None)
def EndElementHandler(self, name):
name = self.fixname(name)
name = self.fixname(name)[0]
self.gen.emitEndElement(name)
def DefaultHandler(self, text):
......
<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>
<?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>
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