class SpoolTag(FilterTag): """ This is a trivial subclass of FilterTag; the only difference is that the name parameter is required. """ signature=Signature(('name', ('filter', None))) tagName='spool'
class HaltTag(EmptyTag): tagName='halt' signature=Signature(()) _top=True def genCode(self, codeStream): codeStream.writeln('raise ReturnValue')
class ImportTag(EmptyTag): signature=Signature(('module', ('items', None), ('as', None))) tagName='import' _top=True def genCode(self, codeStream): """ This supports eight kinds of import: 1. import M :== <:import M:> 2. import M as C :== <:import M as=C:> or <:import "M as C":> 3. from M import X :== <:import M X:> 4. from M import X, Y :== <:import M "X, Y":> 5. from M import X as C :== <:import M X as=C:> 6. from M import * :== <:import M *:> 7. import M1, M2 :== <:import "M1, M2":> 8. import M1 as C, M2, M3 as D :== <:import "M1 as C, M2, M3 as D":> Note that case 6. cannot have an "as" clause. """ module=self._parsed_args['module'] items=self._parsed_args['items'] asClause=self._parsed_args['as'] if items=='*' and asClause is not None: raise STMLSyntaxError, "cannot use 'as' clause with from <module> import *" if items: codeStream.write('from %s import %s ' % (module, items)) else: codeStream.write('import %s' % module) if asClause: codeStream.write('as %s' % asClause) codeStream.write('\n')
class DelTag(EmptyTag): signature=Signature(('name',)) tagName='del' _top=True def genCode(self, codeStream): codeStream.writeln('del %s' % self._parsed_args['name'])
class IncludeComponentTag(ComponentTagBase): tagName='include' signature=Signature(('name',)) def genCode(self, codeStream): handle=self._parsed_args['name'] s='OUTPUT.write(COMPONENT.callIncludeComponent(%r))' % handle codeStream.writeln(s)
class SetTag(EmptyTag): signature=Signature(('name', 'value')) tagName='set' _top=True def genCode(self, codeStream): codeStream.writeln("%s=(%r)" % (self._parsed_args['name'], self._parsed_args['value']))
class CompargsTag(EmptyTag): tagName='compargs' signature=Signature((), 'args', 'kwargs') _top=True def genCode(self, codeStream): args=self._parsed_args['args'] kwargs=self._parsed_args['kwargs'] s="COMPONENT.check_args(%r, %r)" % (args, kwargs) codeStream.writeln(s)
class ElifTag(EmptyTag): tagName='elif' signature=Signature(('expr',)) def genCode(self, codeStream): codeStream.dedent() codeStream.writeln('elif %r:' % self._parsed_args['expr']) codeStream.indent() # in case no other code is generated for the block codeStream.writeln('pass')
class RaiseTag(EmptyTag): signature=Signature((('exc', None),)) tagName='raise' _top=True def genCode(self, codeStream): exc=self._parsed_args['exc'] if exc is None: codeStream.writeln('raise') else: codeStream.writeln('raise (%r)' % exc)
class CallTag(EmptyTag): signature=Signature(('expr',)) tagName='call' _top=True def genCode(self, codeStream): tocall=self._parsed_args['expr'] if not isinstance(tocall, Expr): raise STMLSyntaxError, "argument to call must be an expression" s=tocall.text for l in s.splitlines(): codeStream.writeln(l)
class ExceptTag(EmptyTag): signature=Signature((('exc', None),)) tagName='except' def genCode(self, codeStream): exc=self._parsed_args['exc'] codeStream.dedent() if exc is not None: codeStream.writeln('except %s:' % exc) else: codeStream.writeln('except:') codeStream.indent() codeStream.writeln('pass')
class ValTag(EmptyTag): """ Implementation of the <:val `expr` fmt=`None`:> tag, which outputs a stringified value of its expr argument, formatted by the formatter fmt, if not None. """ signature=Signature(('expr', ('fmt', None))) modules=[('skunk.templating.stml', 'stml')] tagName='val' _top=True def genCode(self, codeStream): expr=self._parsed_args['expr'] fmt=self._parsed_args['fmt'] codeStream.writeln('__h.stml.write_val(%r, OUTPUT, %r)' % (expr, fmt))
class DefaultTag(EmptyTag): signature=Signature(('name', ('value', ''))) tagName='default' _top=True def genCode(self, codeStream): name=self._parsed_args['name'] value=self._parsed_args['value'] codeStream.writeln('try:') codeStream.indent() codeStream.writeln(name) codeStream.dedent() codeStream.writeln('except (NameError, AttributeError):') codeStream.indent() codeStream.writeln('%s=%r' % (name, value)) codeStream.dedent()
class IfTag(BlockTag): localTagDict={'else' : ElseTag, 'elif' : ElifTag} signature=Signature(('expr',)) tagName='if' _top=True def genCode(self, codeStream): codeStream.writeln('if %r:' % self._parsed_args['expr']) codeStream.indent() codeStream.writeln('pass') for k in self.children: if isinstance(k, basestring): codeStream.writeln('OUTPUT.write(%r)' % k) else: k.genCode(codeStream) codeStream.dedent()
class CacheTag(EmptyTag): tagName="cache" signature=Signature((('until', None), ('duration', None))) modules=[('skunk.date.timeutil', '_timeutil')] _top=True def genCode(self, codeStream): args=[self._parsed_args[x] for x in ('until', 'duration')] if args.count(None)!=1: raise ValueError, \ ("exactly one of 'until' and 'duration' arguments" "may be specified") u, d=args if u: s="__expiration=__h._timeutil.convertUntil(%r)" % u else: s="__expiration=__h._timeutil.convertDuration(%r)" % d codeStream.writeln(s)
class ForTag(BlockTag): localTagDict={'else' : ElseTag, 'break' : BreakTag, 'continue' : ContinueTag} signature=Signature(('expr', ('name', 'sequence_item'))) tagName='for' _top=True def genCode(self, codeStream): codeStream.writeln('for %s in %s:'% \ (self._parsed_args['name'], self._parsed_args['expr'])) codeStream.indent() codeStream.writeln('pass') for k in self.children: if isinstance(k, basestring): codeStream.writeln('OUTPUT.write(%r)' % k) else: k.genCode(codeStream) codeStream.dedent()
class UseTag(EmptyTag): """ A tag which imports a tag vocabulary into a single STML component, optionally with a tag prefix. This tag does not generate code, but affects the effective tag vocabulary. The tagdict therefore cannot be a Python expression, but a string indicating the complete __name__ of the tag dictionary, which must be defined in a Python module. """ tagName='use' signature=Signature(('tagdict', ('prefix', None))) _top=True def parse(self, lexer): EmptyTag.parse(self, lexer) tagdict=self._parsed_args['tagdict'] if not isinstance(tagdict, basestring): raise STMLSyntaxError, \ ("tagdict must be a string") prefix=self._parsed_args['prefix'] i=tagdict.rfind('.') if i == -1: raise ValueError, \ "not an importable name" modname=tagdict[:i] objname=tagdict[i+1:] # let ImportError propagate mod=__import__(modname) moremods=modname.split('.')[1:] for m in moremods: mod=getattr(mod, m) # let AttributeError propagate obj=getattr(mod, objname) self.root.useTagLibrary(obj, prefix) def genCode(self, codeStream): pass
class StringComponentTag(ComponentTagBase): tagName='component' signature=Signature(('name', ('cache', 'no'), ('namespace', None), ('expiration', None), ('__args__', None)), None, 'compArgs') def genCode(self, codeStream): handle=self._parsed_args['name'] cachePolicy=self._parsed_args['cache'] compArgs=self._marshal_args() namespace=self._parsed_args['namespace'] expiration=self._parsed_args['expiration'] s=('OUTPUT.write(COMPONENT.callStringComponent(' 'componentHandle=%r, compArgs=%r, ' 'cachePolicy=__h.cache.decode(%r), ' 'expiration=%r, namespace=%r))') % \ (handle, compArgs, cachePolicy, expiration, namespace) codeStream.writeln(s)
class DataComponentTag(ComponentTagBase): tagName='datacomp' signature=Signature(('var', 'name', ('cache', 'no'), ('namespace', None), ('expiration', None), ('__args__', None)), None, 'compArgs') def genCode(self, codeStream): varname=self._parsed_args['var'] handle=self._parsed_args['name'] cachePolicy=self._parsed_args['cache'] compArgs=self._marshal_args() namespace=self._parsed_args['namespace'] expiration=self._parsed_args['expiration'] s=('%s=COMPONENT.callDataComponent(' 'componentHandle=%r, compArgs=%r, cachePolicy=__h.cache.decode(%r), ' 'expiration=%r, namespace=%r)') % \ (varname, handle, compArgs, cachePolicy, expiration, namespace) codeStream.writeln(s)
class Node(object): """ base class for STML tag nodes; also for RootNode, even though it has no tag. Class attributes, which should be shadowed by sub-classes: signature the tag signature. isBlock whether the tag is a block tag or not. localTagDict if this is a block tag, a dictionary of tags that can occur within the block in addition to whatever is globally available modules modules that code generated by this tag may need; they will be imported into the hidden namespace (__h). Either an actual module, a module name, or a tuple (module_or_module_name, module_alias) can appear in this list. tagName the name of the tag. """ signature = Signature(()) isBlock = False localTagDict = {} modules = [] tagName = None __slots__ = [ 'globalTagDict', 'children', 'args', 'kwargs', '_parsed_args', '_token', 'prefix', 'root', 'parent' ] def __init__(self, globalTagDict=None, parent=None, root=None, prefix=None): self.globalTagDict = globalTagDict or {} self.children = [] self.args = [] self.kwargs = {} self._token = None self.prefix = prefix self.root = root self.parent = parent def _init_temporary_namespace(cls, nsObj): """ class method usable by subclasses if they need to initialize the temporary namespace """ pass _init_temporary_namespace = classmethod(_init_temporary_namespace) def parse(self, lexer): """ builds the node tree from the lexer. """ self._parse_tag(lexer) self._parsed_args = self.signature.resolve_arguments( self.args, self.kwargs) if self.isBlock: self._parse_block(lexer) def _parse_tag(self, lexer): # you enter this when the tagname has already been identified curarg = None state = None while 1: try: self._token = token = lexer.next() except StopIteration: if state is None: break else: # the lexer must be broken, it should have # raise a LexicalError earlier raise ParseError("incomplete input, hit EOF in tag") else: if state is None: state = "intag" tokenType = token.tokenType if tokenType == t_TAGNAME: self.handle_tagname(token.text) # this doesn't happen here assert False continue elif tokenType == t_EQUALS: if curarg is None: self.handle_error("unexpected equals sign") state = "expectval" continue elif tokenType == t_EXPR: if state == 'expectval': if curarg in self.kwargs: self.handle_error("duplicate keyword argument") self.kwargs[curarg] = Expr(token.text) curarg = None state = 'intag' else: if curarg is not None: self.args.append(curarg) curarg = Expr(token.text) elif tokenType == t_TAGWORD or tokenType == t_QUOTED_STRING: if state == 'expectval': if curarg in self.kwargs: self.handle_error("duplicate keyword argument") self.kwargs[curarg] = Expr(repr(token.text)) curarg = None state = 'intag' else: if curarg is not None: self.args.append(curarg) curarg = token.text elif tokenType == t_END_TAG: if curarg is not None: self.args.append(curarg) break else: self.handle_error("unexpected token", token) def handle_eof_in_block(self): """ if an EOF occurs inside a block, this handles it, by default, raising an error. """ self.handle_error("hit EOF, expected close tag") def _parse_block(self, lexer): while 1: try: self._token = token = lexer.next() except StopIteration: if self.isBlock: return self.handle_eof_in_block() else: break tokenType = token.tokenType if tokenType == t_TEXT: self.handle_text(token) elif tokenType == t_START_TAG: # ignore, we'll handle this # for t_TAGNAME continue elif tokenType == t_TAGNAME: closed = self.handle_tag(token, lexer) if closed: break else: self.handle_error("unexpected token", token) def _find_tag(self, token, tagName, prefix): """ look up the tag in the global, local, and component tag dictionaries, then in the inherited local tag dictionaries. """ fulltag = token.text try: return self.globalTagDict[fulltag] except KeyError: pass if prefix == self.prefix: try: return self.localTagDict[tagName] except KeyError: pass try: return self.root._extraTagDict[fulltag] except KeyError: pass p = self.parent while p: if p.prefix == prefix: try: return p.localTagDict[tagName] except KeyError: pass p = p.parent self.handle_error("tag not found: %s" % fulltag, token) def handle_text(self, token): self.children.append(token.text) def _split_tagname(self, tagname): if ':' in tagname: return tagname.split(':', 1) else: return None, tagname def handle_tag(self, token, lexer): """ parses any tag in the block, creating child nodes as necessary """ if token.text.startswith('/'): return self.handle_close_tag(token, lexer) else: prefix, tagname = self._split_tagname(token.text) tagNodeClass = self._find_tag(token, tagname, prefix) if tagNodeClass.isBlock: node = tagNodeClass(self.globalTagDict, prefix=prefix, parent=self, root=self.root) else: node = tagNodeClass(parent=self, prefix=prefix, root=self.root) self.children.append(node) node.parse(lexer) return False def handle_close_tag(self, token, lexer): """ handler for a close tag """ prefix, tag = self._split_tagname(token.text[1:]) if self.tagName == tag and self.prefix == prefix: self._token = t = lexer.next() if t.tokenType == t_END_TAG: return True else: self.handle_error("malformed tag") # if we get here self.handle_error('close tag not expected in this context') def handle_error(self, msg, token=None): """ raises a ParseError """ if token is None: token = self._token if token is None: raise ParseError(msg, 0, 0) raise ParseError("%s: %s (%s)" % (msg, token.text, token.tokenType), token.offset, token.lineno)
class FilterTag(BlockTag): """ A block tag that gathers the output inside the block and can: 1. store it in a variable rather than output it directly; 2. apply a filter to it; 3. both; 4. neither. """ signature=Signature((('name', None), ('filter', None))) tagName='filter' modules=['cStringIO', ('skunk.templating.stml', 'stml')] _top=True def _init_temporary_namespace(cls, nsObj): # add the filter stack nsObj._filter_stack=[] _init_temporary_namespace=classmethod(_init_temporary_namespace) def genCode(self, codeStream): name=self._parsed_args.get('name') filter=self._parsed_args.get('filter') if not (name or filter): # just process the intervening tags codeStream.writeln('pass') for k in self.children: if isinstance(k, basestring): codeStream.writeln('OUTPUT.write(%r)' % k) else: k.genCode(codeStream) else: # replace the output stream with a temporary one, # putting the existing stream on a stack codeStream.writeln('__t._filter_stack.append(OUTPUT)') codeStream.writeln('OUTPUT=__h.cStringIO.StringIO()') # process the intervening tags in a try/finally block codeStream.writeln('try:') codeStream.indent() codeStream.writeln('pass') for k in self.children: if isinstance(k, basestring): codeStream.writeln('OUTPUT.write(%r)' % k) else: k.genCode(codeStream) codeStream.dedent() codeStream.writeln('finally:') codeStream.indent() if name is not None: # we have a name, so store the string in a variable # and replace the output stream. codeStream.writeln('%s=OUTPUT.getvalue()' % name) codeStream.writeln('OUTPUT=__t._filter_stack.pop()') codeStream.dedent() # apply filter, if any if filter: codeStream.writeln('%s=__h.stml.get_formatter(%s)(%s)' % \ (name, filter, name)) else: assert filter # fancy switching with the stack to replace the original output stream # and write the formatted value of the temporary output stream to it codeStream.writeln('__t._filter_stack.append(OUTPUT.getvalue())') codeStream.writeln('OUTPUT=__t._filter_stack.pop(-2)') s='OUTPUT.write(__h.stml.get_formatter(%s)(__t._filter_stack.pop()))' codeStream.writeln(s % filter) codeStream.dedent()