Source code for flash.flmake.template

"""Code to generate files, based on a template"""

import string
import re
import os.path
import UserDict

# relative imports needed
from . import setup_globals
from .setup_globals import gvars, SetupError
from .lazy_file import LazyFile


###############################################################

[docs]class Template(UserDict.UserDict): """Generates file filename using template as the template Lines starting with comment in the template are suppressed other lines are % substituted against the given dictionary returns if generatedFile is same as existing file """ def __init__(self,template,comment="##"): UserDict.UserDict.__init__(self) if not os.path.isfile(template): raise SetupError("Template file %s not found" % template) self.template = os.path.abspath(template) self.comment = comment self.check = None # Dont perform checks on what is being accessed self.__getitem = UserDict.UserDict.__getitem__ self.__setitem = UserDict.UserDict.__setitem__ def __setitem__(self,key,value): UserDict.UserDict.__setitem__(self,key,value) if type(value) in [type([]),type(()),type({})]: self.__setitem(self,"COUNT_"+key,len(value)) try: maxlen = max([len(str(x)) for x in value]) except ValueError: maxlen = 0 self.__setitem(self,"MAXLEN_"+key, maxlen) def update(self,dict): for (k,v) in dict.items(): self[k] = v # Fancy printing of list entries def __getitem__(self,key): parts = key.split("|") if len(parts) == 1: # No | found if key.find("!") < 0: #simple variables self.__checksimple(key) return self.__getitem(self,key) else: return self.__printlist(key,"") # we have a nil value var = parts[0] # variable to print nil = parts[1] # print if NULL or empty or 0 if var.find("!") < 0: # we are printing a string or integer self.__checksimple(var) if self[var]: return self.__getitem(self,var) else: return nil # Now we need to handle sequence type with a nil value return self.__printlist(var,nil) # Check if value is a simple datatype def __checksimple(self,key): if not self.check: return if not self.has_key(key): raise SetupError("Variable %s unknown in template %s" % (key,self.template), setup_globals.IMPINFO) tkey = type(self.__getitem(self,key)) if tkey in [type(0),type(""),type(None)]: return gvars.out.put("WARNING: In template %s you are using the variable '%s' as a simple variable" % (self.template,key), setup_globals.IMPINFO) gvars.out.push() gvars.out.put("But it has type %s" % tkey, setup_globals.IMPINFO) gvars.out.pop() # Allow fancy stuff inside %(...)s # Never use self[...] in this code as we are implementing the code for self[...] def __printlist(self,key,nilvalue): parts = key.split("!") if len(parts) > 3: raise SetupError("Bad Template Syntax (%s) in %s\n Found more than 2 !"% (key,self.template)) info = {"num" : 1, # block size "var" : "", # name of list variable "in" : "", # inner separator "out" : "", # outer separator "rpad": "", # pad all entries with spaces to right so they come out as same length } mobj = re.match("^(?P<num>[0-9]*)(?P<var>[a-zA-Z_]*)(?P<rpad> *)$", parts[0]) if not mobj: raise SetupError("Bad template syntax (%s) in %s" % (key,self.template)) info.update(mobj.groupdict()) if not self.has_key(info["var"]): raise SetupError("Unknown variable %s while expanding template %s" % (info["var"],self.templte)) vals = self.__getitem(self,info["var"]) if type(vals) not in [type([]),type(())]: raise SetupError("Variable %s has type %s in template %s -- not a sequence type" % (info["var"],type(vals),self.template)) # Handle trivial case - empty sequence if not vals: return nilvalue # compute block size if info["num"]: info["num"] = int(info["num"]) else: info["num"] = 1 # parse meaning of parts if info["num"] == 1: # pretend block size 2 with inner and outer sep same info["num"] = 2 info["in"] = parts[1] info["out"] = parts[1] else: if len(parts) == 2: raise SetupError("""Bad Template Syntax (%s) in %s For non-trivial block size you must specify inner and outer separators""" % (key,self.template)) info["in"] = parts[1] info["out"]= parts[2] # form the output string count = 0 ans = [] if info["rpad"]: tstr = "%%-%ds"% self.__getitem(self,"MAXLEN_"+info["var"]) else: tstr = "%s" for val in vals: count += 1 ans.append(tstr % val) if count % info["num"] == 0: # end of block ans.append(info["out"]) else: ans.append(info["in"]) # we have an extra separator at the end return string.join(ans[:-1],"") def generate(self,filename): def repl(mobj): s = mobj.group(0) if s == r"\\": return '\\' elif s == r"\t": return '\t' elif s == r"\r": return '\r' elif s == r"\n": return '\n' else: return s infd = open(self.template,"r") # if the out file already exists and is a link, kill it if os.path.islink(filename): os.remove(filename) outfd= LazyFile(filename) length = len(self.comment) self.check = 1 # Perform checks on what is being accessed for line in infd.readlines(): if line[:length] == self.comment: continue # skip comments # perform % substitution and replace \{\rtn} sequences outfd.write( re.sub(r"\\.",repl,line) % self) infd.close() outfd.close() self.check = None # disable checks return outfd.samefile