##################################################
# SPYCE - Python-based HTML Scripting
# Copyright (c) 2002 Rimon Barr.
#
# Refer to spyce.py
# CVS: $Id: spyceTag.py 1174 2006-08-29 17:22:17Z ellisj $
##################################################
__doc__ = '''Spyce tags functionality.'''
import sys, inspect
import spyce, spyceUtil
import spyceException, spyceModule
##################################################
# Spyce tag library
#
def invokeSingleton(api, tagname, memoize=False, **kwargs):
assert isinstance(api, spyce.spyceWrapper), 'expected spyceWrapper; got %s' % api.__class__
spylambda = api.getModule('spylambda')
code = '[[ from spyceException import * ]] <%s %s />' % (tagname, ' '.join(['%s="=%s"' % (key, value) for key, value in kwargs.iteritems()]))
lamb = spylambda.define(','.join(kwargs.iterkeys()), code, memoize)
lamb(**kwargs)
class spyceTagLibrary:
"All Spyce tag libraries should subclass this."
def __init__(self, prefix):
self._prefix = prefix
self._taghash = {}
for tag in self.tags:
self._taghash[tag.name] = tag
def getTag(self, api, name, id, context, attrs, paired, parent=None):
tag = self.getTagClass(name)(id, api, self._prefix, context, attrs, paired, parent)
tag._lib = self
return tag
def getTagClass(self, name):
return self._taghash[name]
# functions to override
tags = []
def start(self):
pass
def finish(self):
pass
##################################################
# Spyce tag
#
class spyceTag:
"All Spyce tags should subclass this."
def __init__(self, id, api, prefix, context, attrs, paired, parent=None):
"Initialize a tag; prefix = current library prefix"
self._id = id
if 0 and api:
assert isinstance(api, spyce.spyceWrapper)
self._api = api
self._prefix = prefix
self._pair = paired
self._parent = parent
self._context = context
self._out = None
self._buffered = 0
if api and 'handler' in attrs:
# api == None means it's just tagchecker, and parent will always be None
ftag = self.getParent('form')
if not ftag:
raise spyceTagSyntaxException('tags with active handlers must be nested inside form:form')
if 'action' in ftag._attrs:
raise spyceTagSyntaxException('parent form action is incompatible with active handlers')
# delete it before it gets to begin()
# subclass can always do own attr checking before calling spyceTag.__init__
del attrs['handler']
self._attrs = self._attrsEval(attrs)
def _attrsEval(self, attrs):
if not self._context: # tagchecker passes None
return attrs
for key, expr in attrs.items():
if expr and expr[0] == '=':
attrs[key] = self.eval(expr[1:])
return attrs
def eval(self, expr):
try:
return eval(expr, self._context)
except NameError, e:
if '.' not in expr:
raise
# maybe it's a module reference
modname = expr.split('.')[0]
try:
mod = self._api.getModule(modname)
except ImportError:
raise e
self._context[modname] = mod
return eval(expr, self._context)
except TypeError:
raise 'Expected python code; unable to evaluate %s' % expr
# setup tag environment (output stream)
def setOut(self, out):
"Set output stream"
self._out = out
def setBuffered(self, buffered):
"Set whether tag is running on a buffer wrt. enclosing scope"
self._buffered = buffered
# accessors
def getPrefix(self):
"Return tag prefix"
return self._prefix
def getAttributes(self):
"Get tag attributes."
return self._attrs
def getPaired(self):
"Return whether this is a paired or singleton tag."
return self._pair
def getParent(self, name=None):
"Get parent tag"
parent = self._parent
if name is not None:
while parent is not None:
if parent.name == name:
break
parent = parent._parent
return parent
def getFullId(self):
id = ''
p = self
# tag compiler restricts handlers to be inside form
while p and p.__class__.__name__ != 'form_form':
if p._id:
id = p._id + id
p = p._parent
return id
def getOut(self):
"Return output stream"
return self._out
def getBuffered(self):
"Get whether tag is running on a buffer wrt. enclosing scope"
return self._buffered
# functions and fields to override
"Code chunk to insert into calling class (code, ref)" # for use by [[! code blocks in compiled tags
classcode = None
"Handlers to insert into calling class"
handlers = None
"The name of this tag!"
name = None
"Whether this tag wants to buffer its body processing"
buffer = 0
"Whether this tag want to conditionally perform body processing"
conditional = 0
"Whether this tag wants to possibly loop body processing"
loops = 0
"Whether this tag wants to handle exceptions"
catches = 0
"Whether end() must (even on exception) get called if begin() completes"
mustend = 0
"Whether this tag wants to export values to calling context (overrides export())"
exports = 0
def syntax(self):
"Check tag syntax"
pass
def begin(self, **kwargs):
"Process start tag; return true to process body (if conditional==1)"
return 1
def export(self):
"""
return dict of any key/value pairs tag wants to propagate into spyceProcess locals.
(Obviously, key must be a string; value may be any object.)
Used e.g. by <for val="x" ...> to send x into parent scope.
_Must set class.exports or export method will not be called._
"""
return None
def body(self, _contents):
"Process tag body; return true to repeat (if loops==1)"
if _contents:
self.getOut().write(_contents)
return 0
def end(self):
"Process end tag"
pass
def catch(self, ex):
"Process any exception thrown by tag (if catches==1)"
raise
class spyceTagPlus(spyceTag):
"An easier spyceTag class to work with..."
def getModule(self, name):
"Return a Spyce module reference"
return self._api.getModule(name)
def parentRequired(self, parentname):
parent = self.getParent(parentname)
if not parent:
raise '%s tag must be used inside a parent %s active tag' % (self.name, parentname)
return parent
def syntaxNonEmpty(self, *names):
for name in names:
try: value = self._attrs[name]
except KeyError: return
if not value:
raise spyceTagSyntaxException('attribute "%s" should not be empty', name)
def syntaxNonEmpty(self, *names):
for name in names:
try: value = self._attrs[name]
except KeyError: return
if not value:
raise spyceTagSyntaxException('attribute "%s" should not be empty', name)
def syntaxNonEmpty(self, *names):
for name in names:
try: value = self._attrs[name]
except KeyError: return
if not value:
raise spyceTagSyntaxException('attribute "%s" should not be empty', name)
def syntaxNonEmpty(self, *names):
for name in names:
try: value = self._attrs[name]
except KeyError: return
if not value:
raise spyceTagSyntaxException('attribute "%s" should not be empty', name)
def syntaxNonEmpty(self, *names):
for name in names:
try: value = self._attrs[name]
except KeyError: return
if not value:
raise spyceTagSyntaxException('attribute "%s" should not be empty', name)
def syntaxValidSet(self, name, validSet):
try: value = self._attrs[name]
except KeyError: return
if value not in validSet:
raise spyceTagSyntaxException('attribute "%s" should be one of: %s'% (name, ', '.join(validSet)))
def syntaxPairOnly(self):
"Ensure that this tag is paired i.e. open/close"
if not self._pair:
raise spyceTagSyntaxException('singleton tag not allowed')
def syntaxSingleOnly(self):
"Ensure that this tag is single i.e. <foo/>"
if self._pair:
raise spyceTagSyntaxException('paired tag not allowed')
##################################################
# Spyce tag syntax checking
#
class spyceTagChecker:
def __init__(self, server):
self._server = server
self._taglibs = {}
self._stack = []
def loadLib(self, libname, libfrom, libas, rel_file, info=None):
if not libas: libas = libname
try:
self._taglibs[(libname, libfrom)] = \
self._server.loadModule(libname, libfrom, rel_file)(libas)
except (SyntaxError, TypeError):
raise
except:
sys.stdout.write('%s\n' % spyceUtil.exceptionString())
raise spyceException.spyceSyntaxError(
'unable to load module: %s (%s)' % (libname, libas), info)
def getTag(self, (libname,libfrom), name, context, attrs, pair, info):
lib = self._taglibs[(libname, libfrom)]
try:
return lib.getTag(None, name, None, None, attrs, pair, None)
except:
spyce.DEBUG(spyceUtil.exceptionString())
raise spyceException.spyceSyntaxError(
'unknown tag "%s:%s"'%(libname, name), info)
def getTagClass(self, (libname, libfrom), name, info):
lib = self._taglibs[(libname, libfrom)]
try:
return lib.getTagClass(name)
except:
spyce.DEBUG(spyceUtil.exceptionString())
s = 'unknown tag "%s:%s" (known tags in %s are: %s)' % (
libname,
name,
libname,
','.join([t.name for t in lib.tags]))
raise spyceException.spyceSyntaxError(s, info)
def startTag(self, (libname,libfrom), name, attrs, pair, info):
tag = self.getTag((libname, libfrom), name, None, attrs, pair, info)
try:
# standard signature validation
(args, varargs, varkw, defaults) = inspect.getargspec(tag.begin)
# args w/ defaults don't need to be checked
if defaults:
n_defaults = len(defaults)
else:
n_defaults = 0
L = args[1:len(args) - n_defaults] # assume self is first
for attr in L:
if attr not in attrs:
raise spyceTagSyntaxException('"%s" tag call missing compulsory "%s" attribute' % (name, attr))
# extra attrs cause an error, if *args/**kwargs not in signature
for attr in attrs:
# spyceCompile checks to make sure 'handler' is ok; tagChecker
# doesn't have the necessary info to make that decision.
if attr not in args and not varargs and not varkw and attr != 'handler':
raise spyceTagSyntaxException('unexpected attribute "%s"' % attr)
# custom validation
error = tag.syntax()
except spyceTagSyntaxException, e:
spyce.DEBUG(spyceUtil.exceptionString())
raise spyceException.spyceSyntaxError(str(e), info)
if error:
raise spyceException.spyceSyntaxError(error, info)
if pair:
self._stack.append( (libname, libfrom, name, info) )
def endTag(self, (libname,libfrom), name, info):
try:
libname1, libfrom1, name1, info1 = self._stack.pop()
except IndexError:
raise spyceException.spyceSyntaxError(
'unmatched close tag', info)
if (libname1,libfrom1,name1) != (libname,libfrom,name):
raise spyceException.spyceSyntaxError(
'unmatched close tag, expected <%s:%s>' % (libname1,name1), info)
def finish(self):
if self._stack:
libname, libfrom, name, info = self._stack.pop()
raise spyceException.spyceSyntaxError(
'unmatched open tag', info)
##################################################
# Spyce tag syntax exception
#
class spyceTagSyntaxException:
def __init__(self, str):
self._str = str
def __repr__(self):
return self._str
|