From: Patrick Canterino Date: Fri, 26 Apr 2013 14:23:35 +0000 (+0000) Subject: template.py funktioniert auch unter Python 2.7. X-Git-Url: https://git.p6c8.net/template-class.git/commitdiff_plain/HEAD?ds=sidebyside template.py funktioniert auch unter Python 2.7. Wir brauchen template27.py deswegen nicht mehr. --- diff --git a/template.py b/template.py index e4f6bbe..0455ed5 100644 --- a/template.py +++ b/template.py @@ -1,12 +1,11 @@ # # Template (Version 2.5) -# (Python 3) # # Klasse zum Parsen von Templates # # Autor: Patrick Canterino -# Letzte Aenderung: 18.4.2013 +# Letzte Aenderung: 26.4.2013 # # Copyright (C) 2002-2013 Patrick Canterino # diff --git a/template27.py b/template27.py deleted file mode 100644 index 26d63de..0000000 --- a/template27.py +++ /dev/null @@ -1,842 +0,0 @@ - -# -# Template (Version 2.5) -# (Python 2.7) -# -# Klasse zum Parsen von Templates -# -# Autor: Patrick Canterino -# Letzte Aenderung: 18.4.2013 -# -# Copyright (C) 2002-2013 Patrick Canterino -# -# Diese Datei kann unter den Bedingungen der "Artistic License 2.0" -# weitergegeben und / oder veraendert werden. -# Siehe: -# http://www.opensource.org/licenses/artistic-license-2.0 -# - -import os.path -import sys - -class Template: - - # __init__() - # - # Konstruktor - # - # Parameter: -keine- - # - # Rueckgabe: -nichts- - - def __init__(self): - self.file = '' - self.template = '' - self.original = '' - self.old_parsing = 0 - self.vars = {} - self.defined_vars = [] - self.loop_vars = {} - - # get_template() - # - # Kompletten Vorlagentext zurueckgeben - # - # Parameter: -keine- - # - # Rueckgabe: Kompletter Vorlagentext (String) - - def get_template(self): - return str(self.template) - - # set_template() - # - # Kompletten Vorlagentext aendern - # - # Parameter: Vorlagentext - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def set_template(self,template): - self.template = str(template) - - # add_text() - # - # Vorlagentext ans Template-Objekt anhaengen - # - # Parameter: Vorlagentext - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def add_text(self,text): - self.set_template(self.get_template()+str(text)) - - # read_file() - # - # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten - # (Text wird an bereits vorhandenen Text angehaengt) - # - # Parameter: 1. Datei zum Einlesen - # 2. Status-Code (Boolean): - # true => {INCLUDE}-Anweisungen nicht verarbeiten - # false => {INCLUDE}-Anweisungen verarbeiten (Standard) - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def read_file(self,file,not_include=0): - self.file = file - - fp = open(file,'r') - content = fp.read() - fp.close() - - self.add_text(content) - self.save_state() - - if not not_include: self.parse_includes() - - # set_var() - # - # Wert einer Variable setzen - # - # Parameter: 1. Name der Variable - # 2. Wert, den die Variable erhalten soll - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def set_var(self,var,content): - if content is None: content = '' - self.vars[var] = content - - # get_var() - # - # Wert einer Variable zurueckgeben - # - # Parameter: (optional) Variablenname - # - # Rueckgabe: Wert der Variable; - # wenn die Variable nicht existiert, false; - # wenn kein Variablenname angegeben wurde, wird ein - # Array mit den Variablennamen zurueckgegeben - - def get_var(self,var=None): - if var is not None: - if self.vars.has_key(var): - return self.vars[var] - else: - return None - else: - return self.vars.keys() - - # set_vars() - # - # Komplettes Variablen-Array mit einem anderen Array ueberschreiben - # - # Parameter: Array - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def set_vars(self,vars): - self.vars = vars - - # add_vars() - # - # Zum bestehenden Variablen-Array weitere Variablen in Form eines Arrays - # hinzufuegen - # - # Parameter: Array - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def add_vars(self,vars): - self.vars.update(vars) - - # set_loop_data() - # - # Daten fuer eine Schleife setzen - # - # Parameter: 1. Name der Schleife - # 2. Array mit den Dictionaries mit den Variablen fuer - # die Schleifendurchgaenge - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def set_loop_data(self,loop,data): - self.loop_vars[loop] = data - - # add_loop_data() - # - # Daten fuer einen Schleifendurchgang hinzufuegen - # - # Parameter: 1. Name der Schleife - # 2. Dictionary mit den Variablen fuer den - # Schleifendurchgang - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def add_loop_data(self,loop,data): - if self.loop_vars.has_key(loop) and type(self.loop_vars[loop]) is list: - self.loop_vars[loop].append(data) - else: - self.loop_vars[loop] = [data] - - # parse() - # - # In der Template definierte Variablen auslesen, Variablen - # ersetzen, {IF}- und {TRIM}-Bloecke parsen - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse(self): - if self.old_parsing: return self.parse_old() - - # Zuerst die Schleifen parsen - - if self.loop_vars and self.loop_vars.keys(): - loops = self.loop_vars.keys() - - for loop in loops: - self.parse_loop(loop) - - # In Template-Datei definierte Variablen auslesen - - self.get_defined_vars() - - # Variablen ersetzen - - vars = self.get_var() - - if vars is not None and type(vars) is list: - self.parse_if_blocks() - self.replace_vars() - - # {TRIM}-Bloecke entfernen - - self.parse_trim_blocks() - - # parse_old() - # - # In der Template definierte Variablen auslesen, Variablen - # ersetzen, {IF}- und {TRIM}-Bloecke parsen - # (alte Methode) - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_old(self): - # Zuerst die Schleifen parsen - - if self.loop_vars and self.loop_vars.keys(): - loops = self.loop_vars.keys() - - for loop in loops: - self.parse_loop(loop) - - # Normale Variablen durchgehen - - if self.get_var(): - vars = self.get_var() - - for var in vars: - val = self.get_var(var) - - self.parse_if_block(var,val) - - if type(val) is list: - self.fillin_array(var,val) - else: - self.fillin(var,val) - - # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert - # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist, - # aber es funktioniert - - self.get_defined_vars() - - for var in self.defined_vars: - val = self.get_var(var) - - self.parse_if_block(var,val) - self.fillin(var,val) - - # {TRIM}-Bloecke entfernen - - self.parse_trim_blocks() - - # fillin() - # - # Variablen durch Text ersetzen - # - # Parameter: 1. Variable zum Ersetzen - # 2. Text, durch den die Variable ersetzt werden soll - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def fillin(self,var,text): - if text is None: text = '' - - template = self.get_template(); - template = template.replace('{'+str(var)+'}',str(text)); - - self.set_template(template); - - # fillin_array() - # - # Variable durch Array ersetzen - # - # Parameter: 1. Variable zum Ersetzen - # 2. Array, durch das die Variable ersetzt werden soll - # 3. Zeichenkette, mit der das Array verbunden werden soll - # (Standard: '') - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def fillin_array(self,var,array,glue=''): - self.fillin(var,str(glue).join(array)) - - # replace_vars() - # - # Variablen eine nach der anderen ersetzen. Sollte in einer Variable eine - # andere Variable auftauchen, so wird diese nicht ersetzt. - # - # Parameter: Array mit zu parsenden Variablen (optional) - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def replace_vars(self,valid=None): - template = self.get_template() - - if valid is None: - valid_vars = self.get_var() - else: - valid_vars = valid - - x = 0 - - while x < len(template): - if template[x] == '{': - for var in valid_vars: - # Pruefen, ob hier eine gueltige Variable beginnt - - if template[x+1:x+len(var)+2] == var + '}': - if type(self.get_var(var)) is list: - content = ''.join(self.get_var(var)) - else: - # Muss es nochmal zum String machen - # Hilft gegen den "ordinal not in range(128)"-Fehler - # Habe aber keine Ahnung, welche neuen Probleme das verursachen koennte - # Bin gespannt... - content = str(self.get_var(var)) - - if content is None: content = '' - - # Daten vor und nach der Variable - - pre = template[0:x] - post = template[len(pre)+2+len(var):] - - # Alles neu zusammensetzen - - template = pre + content + post - - # Zaehler aendern - - x = len(pre + content) - 1 - - x += 1 - - self.set_template(template) - - # to_file() - # - # Template in Datei schreiben - # - # Parameter: Datei-Handle - # - # Rueckgabe: Status-Code (Boolean) - - def to_file(self,handle): - return handle.write(self.get_template()) - - # reset() - # - # Den gesicherten Stand des Template-Textes wiederherstellen - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def reset(self): - self.template = self.original - - # save_state() - # - # Aktuellen Stand des Template-Textes sichern - # (alte Sicherung wird ueberschrieben) - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def save_state(self): - self.original = self.template - - # parse_loop() - # - # Eine Schleife parsen - # - # Parameter: Name der Schleife - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_loop(self,name): - template = self.get_template() - if template.find('{LOOP '+name+'}') == -1: return - - offset = 0 - name_len = len(name) - - while template.find('{LOOP '+name+'}',offset) != -1: - begin = template.find('{LOOP '+name+'}',offset) - - if template.find('{ENDLOOP}',begin+6+name_len) != -1: - end = template.find('{ENDLOOP}',begin+6+name_len) - - block = template[begin:end+9] - content = block[name_len+7:-9] - - parsed_block = '' - - x = 0 - - while x < len(self.loop_vars[name]): - loop_data = self.loop_vars[name][x] - loop_vars = loop_data.keys() - - ctpl = Template() - ctpl.set_template(content) - - for loop_var in loop_vars: - ctpl.set_var(name+'.'+loop_var,loop_data[loop_var]) - - if self.old_parsing: - ctpl.parse_old() - else: - ctpl.parse() - - parsed_block += ctpl.get_template() - - del(ctpl) - x += 1 - - template = template.replace(block,parsed_block) - offset = begin+len(parsed_block) - - else: - break - - self.set_template(template) - - # get_defined_vars() - # - # In der Template-Datei definierte Variablen auslesen - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def get_defined_vars(self): - template = self.get_template() - if template.find('{DEFINE ') == -1: return - - offset = 0 - - while template.find('{DEFINE ',offset) != -1: - begin = template.find('{DEFINE ',offset)+8 - offset = begin - - name = '' - content = '' - - var_open = 0 - name_found = 0 - define_block = 0 - - x = begin - - while x < len(template): - if template[x] == '\012' or template[x] == '\015': - # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird, - # brechen wir mit dem Parsen des Blockes ab - - break - - if var_open == 1: - if template[x] == '"': - # Der Inhalt der Variable ist hier zu Ende - - var_open = 0 - - if template[x+1] == '}': - # Hier ist der Block zu Ende - - if self.get_var(name) is None: - # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist - - self.set_var(name,content) - self.defined_vars.append(name) - - # {DEFINE}-Block entfernen - - pre = template[0:begin-8] - post = template[x+2:] - - template = pre+post - - # Fertig! - - offset = len(pre) - break - - elif template[x] == '\\': - # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen - - if template[x+1] == 'n': - # "\n" in Zeilenumbrueche umwandeln - - content += "\n" - else: - content += template[x+1] - - x += 1 - - else: - content += template[x] - - else: - if name_found == 1: - if var_open == 0: - if template[x] == '"': - var_open = 1 - else: - break - - else: - # Variablennamen auslesen - - if template[x] == '}' and name != '': - # Wir haben einen {DEFINE}-Block - - name_found = 1 - define_found = 1 - - # Alles ab hier sollte mit dem Teil verbunden werden, der das - # {DEFINE} in einer Zeile verarbeitet - - # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch - # nicht noetig sein sollte - - if template.find('{ENDDEFINE}',x) != -1: - end = template.find('{ENDDEFINE}',x) - x += 1 - - content = template[x:end] - - if self.get_var(name) is None: - # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist - - self.set_var(name,content) - self.defined_vars.append(name) - - pre = template[0:begin-8] - post = template[end+11:] - - template = pre + post - - # Fertig! - - offset = len(pre) - break - - else: - break - - elif template[x] != ' ': - name += template[x] - - elif template[x] != '': - name_found = 1 - - else: - break - - x += 1 - - self.set_template(template) - - # parse_if_block() - # - # IF-Bloecke verarbeiten - # - # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht) - # 2. Status-Code (true => Inhalt anzeigen - # false => Inhalt nicht anzeigen - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_if_block(self,name,state,no_negate=0): - name = str(name) - template = self.get_template() - - count = 0; - - while template.find('{IF '+name+'}') >= 0: - count += 1 - - start = template.find('{IF '+name+'}') - tpl_tmp = template[start:] - splitted = tpl_tmp.split('{ENDIF}') - - block = '' # Kompletter bedingter Block - ifs = 0 # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt) - - # {IF} - - x = 0 - - while x < len(splitted): - # Verschachtelungsfehler abfangen - if x == len(splitted)-1: raise TplClassIFNestingError(self.file,name,count) - - ifs += splitted[x].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen - ifs -= 1 # Zaehler um 1 erniedrigen - block += splitted[x]+'{ENDIF}' # Daten zum Block hinzufuegen - - x += 1 - - if ifs == 0: - # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-)) - break - - if_block = block[len(name)+5:-7] # Alles zwischen {IF} und {ENDIF} - - # {ELSE} - - else_block = '' # Alles ab {ELSE} - ifs = 0 # IF-Zaehler - - splitted = if_block.split('{ELSE}'); - - x = 0 - - while x < len(splitted): - ifs += splitted[x].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen - ifs -= splitted[x].count('{ENDIF}') # Vom Zaehler jedes Vorkommen von ENDIF abziehen - - x += 1 - - if ifs == 0: - # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden - - # Aus dem Rest den ELSE-Block zusammenbauen - - y = x - - while y < len(splitted): - else_block += '{ELSE}'+splitted[y] - y += 1 - - if else_block: - if_block = if_block[0:len(if_block)-len(else_block)] - else_block = else_block[6:] - - break - - if state: - replacement = if_block - else: - replacement = else_block - - template = template.replace(block,replacement) - - self.set_template(template) - - # Evtl. verneinte Form parsen - - if not no_negate: - self.parse_if_block('!'+name,not state,1) - - # parse_if_blocks() - # - # IF-Bloecke zu allen definierten Variablen verarbeiten - # - # Parameter: Array mit zu verarbeitenden IF-Bloecken (optional) - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_if_blocks(self,valid=None): - if valid is None: - valid_vars = self.get_var() - else: - valid_vars = valid - - for valid_var in valid_vars: - self.parse_if_block(valid_var,self.get_var(valid_var)) - - # parse_trim_blocks() - # - # {TRIM}-Bloecke parsen - # - # Dieser Parser ist nicht rekursiv, was auch nicht - # noetig sein sollte. - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_trim_blocks(self): - template = self.get_template() - if template.find('{TRIM}') == -1: return - - offset = 0 - - while template.find('{TRIM}',offset) >= 0: - begin = template.find('{TRIM}',offset) - - if template.find('{ENDTRIM}',begin+6) >= 0: - end = template.find('{ENDTRIM}',begin+6) - - block = template[begin:end+9] - content = block[6:-9] - - trimmed = content.strip() - - template = template.replace(block,trimmed) - - offset = begin+len(trimmed) - else: - break - - self.set_template(template) - - # parse_condtag() - # - # Bedingungstags in einem Vorlagentext verarbeiten - # - # Parameter: 1. Tagname - # 2. Status-Code (true => Tag-Inhalt anzeigen - # false => Tag-Inhalt nicht anzeigen - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_condtag(self,condtag,state): - condtag = str(condtag) - template = self.get_template() - - while template.find('<'+condtag+'>') >= 0: - start = template.find('<'+condtag+'>') # Beginn des Blocks - end = template.find('')+len(condtag)+3 # Ende des Blocks - - extract = template[start:end] # Kompletten Bedingungsblock extrahieren... - - if state: - replacement = extract[len(condtag)+2:0-len(condtag)-3] - else: - replacement = '' - - template = template.replace(extract,replacement) # Block durch neue Daten ersetzen - - self.set_template(template) - - # parse_includes() - # - # {INCLUDE}-Anweisungen verarbeiten - # - # Parameter: -nichts- - # - # Rueckgabe: -nichts- (Template-Objekt wird modifiziert) - - def parse_includes(self): - template = self.get_template() - if template.find('{INCLUDE ') == -1: return - - offset = 0 - - while template.find('{INCLUDE ',offset) >= 0: - begin = template.find('{INCLUDE ',offset) - - start = begin+9 - offset = start - long = 0 - - if template[start] == '"': - long = 1 - start += 1 - - file = '' - skip = 0 - - x = start - - while x < len(template): - if template[x] == '\012' or template[x] == '\015': - skip = 1 - break - elif long == 0 and template[x] == ' ': - skip = 1 - break - elif long == 1 and template[x] == '"': - if template[x+1] != '}': skip = 1 - break - elif long == 0 and template[x] == '}': - break - else: - file += template[x] - - x += 1 - - if skip == 1: continue - - if file != '': - filepath = file - - if not os.path.isabs(file): - dir = os.path.dirname(self.file) - if not dir: dir = '.' - filepath = os.path.normpath(dir+'/'+file) - - if os.path.isfile(filepath): - inc = Template() - inc.read_file(file) - - if long == 1: end = start + len(file) + 2 - else: end = start + len(file) + 1 - - pre = template[0:begin] - post = template[end:] - - template = pre+inc.get_template()+post - offset = len(pre)+len(inc.get_template()) - - del(inc) - - self.set_template(template) - -# Klasse zum Erzeugen des Fehlers bei falsch verschachtelten -# {IF}-Bloecken - -class TplClassIFNestingError: - - def __init__(self,file,name,count): - self.file = file - self.name = name - self.count = count - - def __str__(self): - return 'Nesting error found while parsing IF block "'+self.name+'" nr. '+str(self.count)+' in template file "'+self.file+'"' - -# -### Ende ### \ No newline at end of file