#!/bin/env python #********************************************************************** # Copyright (c) 2003 Karl Auerbach, Santa Cruz, California, USA * # All Rights Reserved * # * # Permission to use, copy, modify, and distribute this software and * # its documentation for any purpose and without fee is hereby * # granted, provided that the above copyright notice appear in all * # copies and that both that copyright notice and this permission * # notice appear in supporting documentation, and that the name of * # Karl Auerbach not be used in advertising or publicity pertaining to * # distribution of the software without specific, written prior * # permission. * # * # KARL AUERBACH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * # FITNESS, IN NO EVENT SHALL KARL AUERBACH BE LIABLE FOR ANY SPECIAL, * # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * #********************************************************************** # The above license is the same as the Python license but with the * # date and name of the author(s)/licensor(s) changed. * # See http://www.python.org/doc/Copyright.html * #********************************************************************** #********************************************************************** # Please report bugs to the following e-mail address, which has been * # somewhat obfuscated to ward off spambots - you can cope: * # karl + keyword + cblib.b11f77 at cavebear.com * #********************************************************************** #********************************************************************** # This file originally came from http://www.cavebear.com/cblib/ * #********************************************************************** #********************************************************************** # $Id: katu.py,v 0.4 2005/07/01 08:45:33 karl Exp $ # # $Log: katu.py,v $ # Revision 0.4 2005/07/01 08:45:33 karl # Slight cleanup of the comments and example code. # # Revision 0.3 2004/04/13 06:23:27 karl # Ran through pychecker - not perfect, but better. # # Revision 0.2 2004/03/19 00:45:01 karl # Simplified the InsertFile utility routine. # # Revision 0.1 2004/03/18 23:40:20 karl # Initial rcs check in. # #********************************************************************** import re import types #********************************************************************** # SubstituteParameterStrings #********************************************************************** def SubstituteParameterStrings(Content, Regex, MatchGroup, Dict): """Given a body of content, find and replace all occurrences of a caller-supplied regular expression as defined by a caller-provided dictionary. Parameters: Content - The input text. Regex - A regular expression that defines the keywords. This can be either a compiled regular expression or a string. MatchGroup - An integer, 0..N indicating which "group" resulting from the regular expression should be used as the keyword. (See "Match Objects" in the standard Python "re" module. http://www.python.org/doc/current/lib/match-objects.html) A value of 0 is always safe and indicates that the entire string matched by the regular expression represents the keyword. Any value other than 0 requires that the regular expression create a sufficient number of "group" side effects. (Groups are created using parenthesis in the regular expression.) The following is a regular expression that picks out keyword patterns of the form $(keyword) and creates a group #1 containing only the part: "\$\(([^)\n\r]+)\)" The following is a regular expression that picks out keyword patterns of the form ${keyword} and creates a group #1 containing only the part: "\${([^}\n\r]+)}" An invalid non-zero MatchGroup value may cause an exception. Dict - A dictionary. The keys in the user-provided dictionary are strings that are expected to be the results of the regular expression based scan. (The expected results should take into account the group selection specified by the "MatchGroup" parameter. If keyword is not found in the dictionary, no substition will be made. The values in the user-provided dictionary are of one of three kinds: - Function and lambda types: These will be called with the first parameter being the dictionary key, i.e. the result of the regular expression. This function may be a lambda or a closure, thus obviating the need to have a cookie parameter. These functions should return a string. - List or tuple types in which there at least two items and the first item is a function type. This function may be a lambda or a closure. The function will be called with the first parameter being the dictionary key, i.e. the result of the regular expression. The next parameter will be a tuple made using a [1:] slice, i.e. the original list or tuple with the function/lambda type removed. - Other types - these will be translated to strings using str(). The string that results will be substituted for the *full* keyword as defined by group(0) of the regular expression. Return value: A string with the keywords replaced as indicated. Notes: The function re.sub() is used to scan the content. """ if isinstance(Regex, types.StringType): Regex = re.compile(Regex) #********************************************************************** # __katu_Helper() #********************************************************************** def __katu_Helper(MatchObj, HMatchGroup=MatchGroup, HDict=Dict): """This is a closure that is bound to the present values of MatchGroup and Dict.""" # The following will emit an exception if there is no # such group. # Group number 0 will return the entire string. keyword = MatchObj.group(HMatchGroup) try: dval = HDict[keyword] if isinstance(dval, (types.FunctionType, types.LambdaType)): return dval(keyword) if isinstance(dval, (types.TupleType, types.ListType)): if (len(dval) > 1) and \ isinstance(dval[0], (types.FunctionType, types.LambdaType)): return dval[0](keyword, dval[1:]) else: raise ValueError, "Invalid template substitution dictionary entry" return str(dval) except KeyError: # For keywords not in the dictionary, we do an identity # substitution - self is substituted for self. return MatchObj.group() # We ought to never get to here return MatchObj.group() #********************************************************************** #********************************************************************** return re.sub(Regex, __katu_Helper, Content) #********************************************************************** # InsertFile # Define a utility that will insert a file into a keyword. # The keyword dictionary would have an entry of the form: # "KEYWORD" : [InsertFile, "filename"], #********************************************************************** def InsertFile(keyword, parmlist): """Insert a file into a keyword. """ if not (parmlist and parmlist[0]): return "" try: fd = open(parmlist[0], 'r') except: return "" content = fd.readlines() fd.close() return "".join(content) #********************************************************************** # Test scaffold #********************************************************************** if __name__ == "__main__": input_data = """This is some test input. Here's a line with an embedded $(KEYWORD1) (after keyword here). Here's a line with a trailing $(KEYWORD2) Here's a variation of the above that takes a numeric value: $(KEYWORD2a) Here's a line that uses a function/closure $(KEYWORDF) Here's a line that uses a function/closure $(KEYWORDFA) in a tuple. Here's a line that uses a lamda $(KEYWORDL) Here's a line that uses checked $(KEYWORDC) Here's a line that uses checkKWC $(KEYWORDCC) $(KEYWORD3) This line has a leading keyword. This line has $(KEYWORD4) and $(KEYWORD5) multiple keywords. Here's a line with non-existant keyword $(KEYWORDX). Here's a line that should drag in a copy of this file $(KEYWORDFILE)This is the last line""" print input_data # Here we look for patterns of the form $(keyword) # The resulting group(1) will contain the keyword portion. regstr = "\$\(([^)\n\r]+)\)" regex = re.compile(regstr) fret = "Foo Return" def foo(keyword, result=fret): return keyword+result def footup(keyword, tup, result=fret): return keyword+str(tup)+result def checked(keyword, parms): if keyword == parms[0]: return " checked" return "" def CheckedHtml(keyword, testword): if keyword == testword: return " checked" return "" def checkKWC(keyword, testword="KEYWORDCC"): return CheckedHtml(keyword, testword) fret = "Meow Return" testdict = { \ "KEYWORD1" : "key1", \ "KEYWORD2" : "key2", \ "KEYWORD2a" : 2, \ "KEYWORDF" : foo(fret), \ "KEYWORDFA" : [footup, "Woof"], \ "KEYWORDL" : lambda x="moo": foo(x), \ "KEYWORDC" : [checked, "KEYWORDC", "Bozo"],\ "KEYWORDCC" : checkKWC, \ "KEYWORD3" : "key3", \ "KEYWORD4" : "key4", \ "KEYWORD5" : "key5", \ "KEYWORD6" : "key6", \ "KEYWORD7" : "key7", \ "KEYWORDFILE" : [InsertFile, "katu.py"], } fret = "Roar Return" result = SubstituteParameterStrings(input_data, regex, 1, testdict) print result