def throw(self, error, info=None, output=None): """Raises a TemplateException. This method may be passed an existing TemplateException object; a single value containing an error message which is used to instantiate a TemplateException of type 'None'; or a pair of values representing the exception type and info from which a TemplateException object is instantiated. e.g. context.throw(exception) context.throw("I'm sorry Dave, I can't do that") context.throw('denied', "I'm sorry Dave, I can't do that") An optional third parameter can be supplied in the last case which is a reference to the current output buffer containing the results of processing the template up to the point at which the exception was thrown. The RETURN and STOP directives, for example, use this to propagate output back to the user, but it can safely be ignored in most cases. """ error = unscalar(error) info = unscalar(info) if isinstance(error, TemplateException): raise error elif info is not None: raise TemplateException(error, info, output) else: raise TemplateException("None", error or "", output)
def insert(self, files): """Insert the contents of a file without parsing.""" # TODO: Clean this up; unify the way "files" is passed to this routine. files = unscalar(files) if is_seq(files): files = unscalar_list(files) else: files = [unscalar(files)] prefix = providers = text = None output = cStringIO.StringIO() for file in files: prefix, name = split_prefix(file) if prefix: providers = self.__prefix_map.get(prefix) if not providers: self.throw(ERROR_FILE, "no providers for file prefix '%s'" % prefix) else: providers = self.__prefix_map.get("default") or self.__load_templates for provider in providers: try: text = provider.load(name, prefix) except Exception, e: self.throw(ERROR_FILE, str(e)) if text is not None: output.write(text) break else: self.throw(ERROR_FILE, "%s: not found" % file)
def next(self): if not self.ready: self.ready = True if self.data: return unscalar(self.data[0]) elif self.advance(): return unscalar(self.data[self.index]) raise StopIteration
def __next__(self): if not self.ready: self.ready = True if self.data: return unscalar(self.data[0]) elif self.advance(): return unscalar(self.data[self.index]) raise StopIteration
def get(self, ident, args=None): """Returns the value for an variable stored in the stash. The variable may be specified as a simple string, e.g. 'foo', or as an array reference representing compound variables. In the latter case, each pair of successive elements in the list represent a node in the compound variable. The first is the variable name, the second a list of arguments or 0 if undefined. So, the compound variable [% foo.bar('foo').baz %] would be represented as the list [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the identifier or an empty string if undefined. """ ident = util.unscalar(ident) root = self if isinstance(ident, str) and ident.find(".") != -1: ident = [y for x in ident.split(".") for y in (re.sub(r"\(.*$", "", x), 0)] if isinstance(ident, (list, tuple)): for a, b in util.chop(ident, 2): result = self.__dotop(root, a, b) if result is not None: root = result else: break else: result = self.__dotop(root, ident, args) if result is None: result = self.undefined(ident, args) return util.PerlScalar(result)
def __assign(self, root, item, args=None, value=None, default=False): """Similar to __dotop, but assigns a value to the given variable instead of simply returning it. The first three parameters are the root item, the item and arguments, as per __dotop, followed by the value to which the variable should be set and an optional 'default' flag. If set true, the variable will only be set if currently false. """ item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self if root is None or item is None: return None elif self.PRIVATE and re.search(self.PRIVATE, item): return None elif isinstance(root, dict) or atroot: if not (default and root.get(item)): root[item] = value return value elif isinstance(root, (list, tuple)) and re.match(r"-?\d+$", str(item)): item = int(item) if not (default and 0 <= item < len(root) and root[item]): root[item] = value return value elif util.is_object(root): if not (default and getattr(root, item)()): args.append(value) return getattr(root, item)(*args) else: raise Error("don't know how to assign to %s.%s" % (root, item)) return None
def get(self, ident, args=None): """Returns the value for an variable stored in the stash. The variable may be specified as a simple string, e.g. 'foo', or as an array reference representing compound variables. In the latter case, each pair of successive elements in the list represent a node in the compound variable. The first is the variable name, the second a list of arguments or 0 if undefined. So, the compound variable [% foo.bar('foo').baz %] would be represented as the list [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the identifier or an empty string if undefined. """ ident = util.unscalar(ident) root = self if isinstance(ident, str) and ident.find(".") != -1: ident = [ y for x in ident.split(".") for y in (re.sub(r"\(.*$", "", x), 0) ] if isinstance(ident, (list, tuple)): for a, b in util.chop(ident, 2): result = self.__dotop(root, a, b) if result is not None: root = result else: break else: result = self.__dotop(root, ident, args) if result is None: result = self.undefined(ident, args) return util.PerlScalar(result)
def set(self, ident, value, default=False): """Updates the value for a variable in the stash. The first parameter should be the variable name or list, as per get(). The second parameter should be the intended value for the variable. The third, optional parameter is a flag which may be set to indicate 'default' mode. When set true, the variable will only be updated if it is currently undefined or has a false value. The magical 'IMPORT' variable identifier may be used to indicate that value is a dictionary whose values should be imported. Returns the value set, or an empty string if not set (e.g. default mode). In the case of IMPORT, returns the number of items imported from the hash. """ root = self ident = util.unscalar(ident) value = util.unscalar(value) # ELEMENT: { if isinstance(ident, str) and ident.find(".") >= 0: ident = [ y for x in ident.split(".") for y in (re.sub(r"\(.*$", "", x), 0) ] if isinstance(ident, (list, tuple)): chopped = list(util.chop(ident, 2)) for i in range(len(chopped) - 1): x, y = chopped[i] result = self.__dotop(root, x, y, True) if result is None: # last ELEMENT return "" else: root = result result = self.__assign(root, chopped[-1][0], chopped[-1][1], value, default) else: result = self.__assign(root, ident, 0, value, default) if result is None: return "" else: return result
def set(self, ident, value, default=False): """Updates the value for a variable in the stash. The first parameter should be the variable name or list, as per get(). The second parameter should be the intended value for the variable. The third, optional parameter is a flag which may be set to indicate 'default' mode. When set true, the variable will only be updated if it is currently undefined or has a false value. The magical 'IMPORT' variable identifier may be used to indicate that value is a dictionary whose values should be imported. Returns the value set, or an empty string if not set (e.g. default mode). In the case of IMPORT, returns the number of items imported from the hash. """ root = self ident = util.unscalar(ident) value = util.unscalar(value) # ELEMENT: { if isinstance(ident, str) and ident.find(".") >= 0: ident = [y for x in ident.split(".") for y in (re.sub(r"\(.*$", "", x), 0)] if isinstance(ident, (list, tuple)): chopped = list(util.chop(ident, 2)) for i in range(len(chopped)-1): x, y = chopped[i] result = self.__dotop(root, x, y, True) if result is None: # last ELEMENT return "" else: root = result result = self.__assign(root, chopped[-1][0], chopped[-1][1], value, default) else: result = self.__assign(root, ident, 0, value, default) if result is None: return "" else: return result
def filter(self, name, args=None, alias=None): """Similar to plugin() above, but querying the LOAD_FILTERS providers to return filter instances. An alias may be provided which is used to save the returned filter in a local cache. """ name = unscalar(name) args = unscalar_list(args or []) filter = None if not args and isinstance(name, str): filter = self.__filter_cache.get(name) if filter: return filter for provider in self.__load_filters: filter = provider.fetch(name, args, self) if filter: if alias: self.__filter_cache[alias] = filter return filter self.throw("%s: filter not found" % name)
def __dotop(self, root, item, args=None, lvalue=False): """This is the core 'dot' operation method which evaluates elements of variables against their root. All variables have an implicit root which is the stash object itself. Thus, a non-compound variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is '(stash.)foo.bar'. The first parameter is the current root, initially the stash itself. The second parameter contains the name of the variable element, e.g. 'foo'. The third optional parameter is a list of any parenthesised arguments specified for the variable, which are passed to sub-routines, object methods, etc. The final parameter is an optional flag to indicate if this variable is being evaluated on the left side of an assignment (e.g. foo.bar.baz = 10). When set true, intermediated dictionaries will be created (e.g. bar) if necessary. Returns the result of evaluating the item against the root, having performed any variable "magic". The value returned can then be used as the root of the next __dotop() in a compound sequence. Returns None if the variable is undefined. """ root = util.unscalar(root) item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self result = None # return undef without an error if either side of dot is unviable if root is None or item is None: return None # or if an attempt is made to access a private member, starting _ or . if (self.PRIVATE and isinstance(item, str) and re.search(self.PRIVATE, item)): return None found = True isdict = isinstance(root, dict) if atroot or isdict: # if root is a regular dict or a Template::Stash kinda dict (the # *real* root of everything). We first lookup the named key # in the hash, or create an empty hash in its place if undefined # and the lvalue flag is set. Otherwise, we check the HASH_OPS # pseudo-methods table, calling the code if found, or return None if isdict: # We have to try all these variants because Perl hash keys are # stringified, but Python's aren't. try: value = root[item] except (KeyError, TypeError): try: value = root[str(item)] except (KeyError, TypeError): try: value = root[int(item)] except (KeyError, TypeError, ValueError): value = None else: value = root[item] if value is not None: if callable(value): result = value(*args) else: return value elif lvalue: # we create an intermediate hash if this is an lvalue root[item] = {} return root[item] # ugly hack: only allow import vmeth to be called on root stash else: try: value = self.HASH_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if (value and not atroot) or item == "import": result = value(root, *args) else: try: return _slice(root, item) except TypeError: found = False elif isinstance(root, (list, tuple, util.Sequence)): # if root is a list then we check for a LIST_OPS pseudo-method # or return the numerical index into the list, or None if isinstance(root, util.Sequence): root = root.as_list() try: value = self.LIST_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if value: result = value(root, *args) else: try: value = root[int(item)] except TypeError: sliced = [] try: return _slice(root, item) except TypeError: pass except IndexError: return None else: if callable(value): result = value(*args) else: return value elif util.is_object(root): try: value = getattr(root, item) except (AttributeError, TypeError): # Failed to get object method, so try some fallbacks. try: func = self.HASH_OPS[item] except (KeyError, TypeError): pass else: return func(root.__dict__, *args) else: if callable(value): return value(*args) else: return value elif item in self.SCALAR_OPS and not lvalue: result = self.SCALAR_OPS[item](root, *args) elif item in self.LIST_OPS and not lvalue: result = self.LIST_OPS[item]([root], *args) elif self.__debug: raise Error("don't know how to access [%r].%s" % (root, item)) else: result = [] if not found and self.__debug: raise Error("%s is undefined" % (item,)) elif result is not None: return result elif self.__debug: raise Error("%s is undefined" % (item,)) else: return None
def __dotop(self, root, item, args=None, lvalue=False): """This is the core 'dot' operation method which evaluates elements of variables against their root. All variables have an implicit root which is the stash object itself. Thus, a non-compound variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is '(stash.)foo.bar'. The first parameter is the current root, initially the stash itself. The second parameter contains the name of the variable element, e.g. 'foo'. The third optional parameter is a list of any parenthesised arguments specified for the variable, which are passed to sub-routines, object methods, etc. The final parameter is an optional flag to indicate if this variable is being evaluated on the left side of an assignment (e.g. foo.bar.baz = 10). When set true, intermediated dictionaries will be created (e.g. bar) if necessary. Returns the result of evaluating the item against the root, having performed any variable "magic". The value returned can then be used as the root of the next __dotop() in a compound sequence. Returns None if the variable is undefined. """ root = util.unscalar(root) item = util.unscalar(item) args = util.unscalar_list(args) atroot = root is self result = None # return undef without an error if either side of dot is unviable if root is None or item is None: return None # or if an attempt is made to access a private member, starting _ or . if (self.PRIVATE and isinstance(item, str) and re.search(self.PRIVATE, item)): return None found = True isdict = isinstance(root, dict) if atroot or isdict: # if root is a regular dict or a Template::Stash kinda dict (the # *real* root of everything). We first lookup the named key # in the hash, or create an empty hash in its place if undefined # and the lvalue flag is set. Otherwise, we check the HASH_OPS # pseudo-methods table, calling the code if found, or return None if isdict: # We have to try all these variants because Perl hash keys are # stringified, but Python's aren't. try: value = root[item] except (KeyError, TypeError): try: value = root[str(item)] except (KeyError, TypeError): try: value = root[int(item)] except (KeyError, TypeError, ValueError): value = None else: value = root[item] if value is not None: if callable(value): result = value(*args) else: return value elif lvalue: # we create an intermediate hash if this is an lvalue root[item] = {} return root[item] # ugly hack: only allow import vmeth to be called on root stash else: try: value = self.HASH_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if (value and not atroot) or item == "import": result = value(root, *args) else: try: return _slice(root, item) except TypeError: found = False elif isinstance(root, (list, tuple, util.Sequence)): # if root is a list then we check for a LIST_OPS pseudo-method # or return the numerical index into the list, or None if isinstance(root, util.Sequence): root = root.as_list() try: value = self.LIST_OPS.get(item) except TypeError: # Because item is not hashable, presumably. value = None if value: result = value(root, *args) else: try: value = root[int(item)] except TypeError: sliced = [] try: return _slice(root, item) except TypeError: pass except IndexError: return None else: if callable(value): result = value(*args) else: return value elif util.is_object(root): try: value = getattr(root, item) except (AttributeError, TypeError): # Failed to get object method, so try some fallbacks. try: func = self.HASH_OPS[item] except (KeyError, TypeError): pass else: return func(root.__dict__, *args) else: if callable(value): return value(*args) else: return value elif item in self.SCALAR_OPS and not lvalue: result = self.SCALAR_OPS[item](root, *args) elif item in self.LIST_OPS and not lvalue: result = self.LIST_OPS[item]([root], *args) elif self.__debug: raise Error("don't know how to access [%r].%s" % (root, item)) else: result = [] if not found and self.__debug: raise Error("%s is undefined" % (item, )) elif result is not None: return result elif self.__debug: raise Error("%s is undefined" % (item, )) else: return None
def view(self, params=None): """Create a new View object bound to this context.""" from template.view import View return View(self, unscalar(params))
def process(self, template, params=None, localize=False): """Processes the template named or referenced by the first parameter. The optional second parameter may reference a dictionary of variable definitions. These are set before the template is processed by calling update() on the stash. Note that, unless the third parameter is true, the context is not localised and these, and any other variables set in the template will retain their new values after this method returns. The third parameter is in place so that this method can handle INCLUDE calls: the stash will be localized. # Returns the output of processing the template. Errors are raised as TemplateException objects. """ template = util.listify(unscalar(template)) params = unscalar(params) compileds = [] for name in template: compileds.append(self.template(name)) if localize: self.__stash = self.__stash.clone(params) else: self.__stash.update(params) output = cStringIO.StringIO() try: # save current component try: component = self.__stash.get("component") except: component = None for name, compiled in zip(template, compileds): if not callable(compiled): element = compiled else: element = { "name": isinstance(name, str) and name or "", "modtime": time.time() } if isinstance(component, Document): # FIXME: This block is not exercised by any test. elt = Accessor(element) elt["caller"] = component.name elt["callers"] = getattr(component, "callers", []) elt["callers"].append(component.name) self.__stash.set("component", element) if not localize: # merge any local blocks defined in the Template::Document # info our local BLOCKS cache if isinstance(compiled, Document): tblocks = compiled.blocks() if tblocks: self.__blocks.update(tblocks) if callable(compiled): tmpout = compiled(self) elif util.can(compiled, "process"): tmpout = compiled.process(self) else: self.throw("file", "invalid template reference: %s" % compiled) if self.__trim: tmpout = tmpout.strip() output.write(tmpout) # pop last item from callers if isinstance(component, Document): elt["callers"].pop() self.__stash.set("component", component) finally: if localize: # ensure stash is delocalised before dying self.__stash = self.__stash.declone() return output.getvalue()
def Create(expr): expr = unscalar(expr) if isinstance(expr, Iterator): return expr else: return Config.iterator(expr)