Seite 1 von 1
String-Formatter finden...
Verfasst: Samstag 1. Oktober 2005, 21:52
von jens
Gibt's eine bessere/direktere Möglichkeit an die Platzhalter in einem Text zu kommen, als:
Code: Alles auswählen
import re
txt = "bla bla %(test1)s und bla %(test2)s xyz %(test3)s"
print re.findall(r"%\(.*?\)s", txt)
Ausgabe:
Verfasst: Samstag 1. Oktober 2005, 22:35
von BlackJack
Ich wüsste keine, aber Dein regulärer Ausdruck ist etwas zu einfach wenn Du wirklich alle Platzhalter finden willst. Es ja nicht nur '%s' und man kann noch die Parameter zum formatieren einbauen, also z.B. '%(name)10s %(pi).2f' usw.
Verfasst: Samstag 1. Oktober 2005, 22:37
von jens
BlackJack hat geschrieben:Dein regulärer Ausdruck ist etwas zu einfach
Da hast du recht, aber erstmal gibt's bei mir nur die einfache %(name)s Version...
Verfasst: Sonntag 2. Oktober 2005, 08:35
von henning
Was meinst du mit einfachere Methode?
Was willst du denn mit den Platzhaltern machen?
Wenn du sie durch Werte ersetzen willst, solltest du natürlich den %-Operator verwenden.
Wenn du nen längeren Text hast und wissen willst, welche Platzhalter darin verlangt werden und du den Text ändern kannst, würde ich eine Template-Engine empfehlen, z.B.
cheetah.
Du kannst natürlich auch sowas machen (ungetestet):
Code: Alles auswählen
class FindKey(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.found_keys = {}
def __getattr__(self, attr):
self.found_keys[attr] = True
return ""
txt = "bla bla %(test1)s und bla %(test2)s xyz %(test3)s"
f = FindKey()
dumy = txt % f
print f.found_keys.keys()
Ich weiß nicht, ob das eleganter oder direkter als ne re ist, aber auf jeden Fall findet es alle Platzhalter.
Achso: Wenn in txt nach was anderem als strings gefragt wird, solltest du bei __getattr__ natürlich was zurückgeben, was sich zumindest problemlos casten lässt.
Verfasst: Sonntag 2. Oktober 2005, 11:28
von jens
Ich brauche das ganze zum Fehler-Handling. Um besser Anzeigen zu können, wo der Fehler steckt
Ich werd erstmal bei der re-Lösung bleiben, weil's kleiner ist und bisher nur die eine Form %(name)s vorkommt.
Werd aber deine Lösung mal im Hinterkopf behalten, wer weiss...
Verfasst: Sonntag 2. Oktober 2005, 14:51
von Joghurt
Verfasst: Sonntag 2. Oktober 2005, 19:33
von jens
Joghurt hat geschrieben:
Also es geht natürlich mal wieder um PyLucid. Dort gibt es interne-Seiten, die halt String-Formater als Platzhalter beinhalten. Die Seiten kann der Admin allerdings editieren. Wenn er nun einen String-Formater löscht/umbenennt, funktioniert die internal_page nicht mehr richtig.
In dem Fall ist es hilfreich einen gescheiten Fehler anzuzeigen
Verfasst: Montag 19. Dezember 2005, 17:49
von jens
Das ist meine aktuelle Version um bei einem "not enough arguments for format string" Fehler, mehr Informationen auszugeben.
Dabei ist content, ein String mit den String-Formatter-Platzhalter und page_dict das Dict. mit den Angaben:
Code: Alles auswählen
import sys
def test(content, page_dict):
try:
print content % page_dict
except Exception, e:
import re
content_placeholder = re.findall(r"%\((.*?)\)s", content)
content_placeholder.sort()
given_placeholder = page_dict.keys()
given_placeholder.sort()
diff_placeholders = []
for i in content_placeholder:
if (not i in given_placeholder) and (not i in diff_placeholders):
diff_placeholders.append(i)
for i in given_placeholder:
if (not i in content_placeholder) and (not i in diff_placeholders):
diff_placeholders.append(i)
diff_placeholders.sort()
raise Exception(
"%s: '%s': Can't fill internal page '%s'. \
*** placeholder in internal page: %s *** given placeholder for that page: %s *** diff: %s" % (
sys.exc_info()[0], e, internal_page_name, content_placeholder, given_placeholder, diff_placeholders
)
)
internal_page_name = "KeinName"
test(
content = "Bla %(platzhalter1)s blup %(Platzhalter2)s...",
page_dict = {
"platzhalter1": "jau",
"PPPaltzhalter": "Fehler"
}
)
Ausgabe:
Exception: exceptions.KeyError: ''Platzhalter2'': Can't fill internal page 'KeinName'. *** placeholder in internal page: ['Platzhalter2', 'platzhalter1'] *** given placeholder for that page: ['PPPaltzhalter', 'platzhalter1'] *** diff: ['PPPaltzhalter', 'Platzhalter2']
Was mir nicht gefällt sind die beiden for-Schleifen um diff_placeholders zusammen zu bauen...
Verfasst: Montag 19. Dezember 2005, 17:56
von henning
Ich finde meine Version schöner
Verfasst: Montag 19. Dezember 2005, 18:57
von jens
henning hat geschrieben:Ich finde meine Version schöner
Jein... Deine Funktioniert aber nur, wenn in txt und in t die Format-String richtig geschrieben und vollständig erhalten sind...
Meine *Fehler*abfrage ist aber für den Fall, das halt die Operatoren nicht passen!
Verfasst: Montag 19. Dezember 2005, 19:02
von Leonidas
Eine andere Möglichkeit wäre, str.__mod__ selbst zu implementieren und dort ein anderes Error-Handling zu machen, welches bessere Fehlermeldungen wirft.
Verfasst: Montag 19. Dezember 2005, 19:39
von Joghurt
henning hat geschrieben:Ich finde meine Version schöner
Man sollte allgemein so Programmieren, dass man nicht versucht, die Ursachen für einen Fehler abzufangen (da man immer etwas übersehen kann). Besser ist es, und das ist ja auch die Pythonphilosophie, auf Fehler zu reagieren, und das macht Jens ja. Von daher finde ich seine Lösung auch "eleganter"
Beispiel:
Code: Alles auswählen
# Schauen ob Datei existiert
if os.path.isfile(datei):
file = open(datei)
... etc ...
else:
print "Datei nicht gefunden
ist schlecht, denn das fängt nicht die Möglichkeit ab, dass die Datei nicht lesbar ist, etc. besser
Code: Alles auswählen
try:
file = open(datei)
... etc ...
except:
print "Fehler beim lesen"
Verfasst: Montag 19. Dezember 2005, 20:45
von joe
Joghurt hat geschrieben:besser
Code: Alles auswählen
try:
file = open(datei)
... etc ...
except:
print "Fehler beim lesen"
Immer nur ausnahmen abfangen, die man auch erwartet (also IOError beim lesen von dateien). Wenn z.B. open() vorher versehentlich überschrieben wurde, wird man oben mit einer falschen fehlermeldung in die irre geführt. Besser:
Code: Alles auswählen
open = "bla"
try:
f = open(datei)
... etc ...
except IOError:
print "Fehler beim lesen"
joe
Verfasst: Montag 19. Dezember 2005, 20:52
von Joghurt
Ja, hast ja Recht. Nur wenn jemand schon open überschreibt, kann er sie auch so modifizieren, dass sie bei jeder Exception ein IOError zurückgibt
Verfasst: Dienstag 20. Dezember 2005, 08:21
von jens
Also ich komm irgendwie doch nicht weiter...
Es kommt immer noch zu einem 'not enough arguments for format string' Fehler
Trotz meiner Fehlermeldung kann ich nicht erkennen woran es liegt
Somit ist es vielleicht doch keine schlechte Idee, tiefer anzufangen. d.H. vielleicht den String-Operator direkt zu manipulieren und darin eine Fehlermeldung einzubauen...
Allerdings weiß ich nicht wirklich wo ich ansetzten kann
Verfasst: Dienstag 20. Dezember 2005, 09:03
von jens
Nun hab ich es... Ich hab ein %-zeichen vergessen, welches eigentlich als %% Escaped werden muß...
Code: Alles auswählen
import sys
def test(content, page_dict):
try:
print content % page_dict
except Exception, e:
import re
content_placeholder = []
for i in re.findall(r"%\((.*?)\)s", content):
if not i in content_placeholder:
content_placeholder.append(i)
content_placeholder.sort()
given_placeholder = page_dict.keys()
given_placeholder.sort()
diff_placeholders = []
for i in content_placeholder:
if (not i in given_placeholder) and (not i in diff_placeholders):
diff_placeholders.append(i)
for i in given_placeholder:
if (not i in content_placeholder) and (not i in diff_placeholders):
diff_placeholders.append(i)
diff_placeholders.sort()
raise Exception(
"%s: '%s': Can't fill internal page '%s'. \n\
*** %s placeholder in internal page: %s \n\
*** %s given placeholder for that page: %s \n\
*** diff: %s" % (
sys.exc_info()[0], e, internal_page_name,
len(content_placeholder), content_placeholder,
len(given_placeholder), given_placeholder,
diff_placeholders
)
)
internal_page_name = "KeinName"
test(
content = "Bla %(platzhalter1)s blup %<-FEHLER..",
page_dict = {
"platzhalter1": "jau",
}
)
In dem Fall kommt nämlich keine gute Fehlermeldung zustande:
Code: Alles auswählen
Exception: exceptions.TypeError: 'not enough arguments for format string': Can't fill internal page 'KeinName'.
*** 1 placeholder in internal page: ['platzhalter1']
*** 1 given placeholder for that page: ['platzhalter1']
*** diff: []
Wie könnte man das besser machen???
btw. Es gibt auch
Template strings, die eine bessere Fehlermeldung haben soll... Gibt's allerdings erst seit Python 2.4
Verfasst: Dienstag 20. Dezember 2005, 12:44
von jens
Ich suche gerade nach eine RE für die Aufgabe:
Code: Alles auswählen
import re
txt = """Soll auf alle %-Zeichen treffen, außer %(Beispiel)s und %%
Wobei ein %% quasi Escaped ist.
Soll aber auch auf %(nicht abgeschossene Klammern treffen...
"""
class FindKeys:
def __init__(self, txt):
pattern = re.compile(r"([^%]%[^%(])")
pattern.sub(self.handle, txt)
def handle(self, matchobj):
print matchobj
print matchobj.pos
print matchobj.group(0)
FindKeys(txt)
Allerdings klappt die RE bei %(nicht abgeschossene Klammern, nicht
Außerdem gibt matchobj.pos nicht wirklich die Position wieder... Es ist immer =0 ?!?!?
Denn ich möchte eigentlich die Position wissen, um den Treffer mit Textteilen vor und hinter dem Treffer anzeigen zu können...
Eine andere Variante wäre re.split() zu nehmen und die Ausgabe dann aus dem Ergebniss zu bauen... Aber das ist irgendwie dumm...
Verfasst: Dienstag 20. Dezember 2005, 13:55
von jens
Nun hab ich mir was gebastelt:
Code: Alles auswählen
class Find_StringOperators:
"""
Sucht in einem String nach %-StringOperatoren.
Dabei wird zwischen richtigen und falschen unterschieden.
Dient zur besseren Fehlerausgabe, bei String Operationen.
Test-Text:
----------
Hier ist ein Beispieltext mit %(richtigen)s Platzhaltern.
Aber auch mit %(falschen, da die hier Klammer nicht geschlossen ist.
Außerdem müßen einzelne %-Zeichen, immer escaped werden. Das wird
mit doppelten %% Zeichen gemacht, die nach dem String-Operation,
bei dem die %(Platzhalter)s durch Daten aus einem Dict ersetzt werden,
wieder zu einfachen %-Zeichen umgewandelt werden.
"""
cutout = 20
def __init__(self, txt):
self.correct_hit_pos = []
self.incorrect_hit_pos = []
self.txt = txt
# alle %-Zeichen, die nicht mit %%-Escaped sind
pattern = re.compile(r"([^%]%[^%])")
pattern.sub(self._incorrect_hit, txt)
# Richtig %(formatierte)s String
pattern = re.compile(r"([^%]%\(.*?\)s)")
pattern.sub(self._correct_hit, txt)
def _incorrect_hit(self, matchobj):
self.incorrect_hit_pos.append(matchobj.start())
def _correct_hit(self, matchobj):
self.incorrect_hit_pos.remove(matchobj.start())
self.correct_hit_pos.append(matchobj.start())
#_______________________________________________________________________
# Zugriff auf die Daten
def get_incorrect_pos(self):
"Start- & End-Liste der falschen %-Operatoren"
return self.get_pos(self.incorrect_hit_pos)
def get_correct_pos(self):
"Start- & End-Liste der richtigen %-Operatoren"
return self.get_pos(self.correct_hit_pos)
def get_pos(self, poslist):
"""
Wandelt aus der Positionsliste eine Liste mit
Start- und End-Positionen für einen Text-Slice
"""
results = []
for pos in poslist:
start = pos - self.cutout
end = pos + self.cutout
if start<0:
start = 0
if end>len(self.txt):
end = len(self.txt)
results.append((start, end))
return results
def slice_pos_list(self, pos_list):
"""
Liefert eine Liste der Textstellen zurück.
"""
result = []
for start,end in pos_list:
result.append(
"...%s..." % self.txt[start:end].encode("String_Escape")
)
return result
#_______________________________________________________________________
# Debug
def debug_results(self):
print "self.incorrect_hit_pos:", self.incorrect_hit_pos
print "incorrect_pos:", self.get_incorrect_pos()
print ">>> Textstellen mit falsche %-Zeichen im Text:"
for i in self.slice_pos_list(self.get_incorrect_pos()):
print i
print
print "self.correct_hit_pos:", self.correct_hit_pos
print "correct_pos:", self.get_correct_pos()
print ">>> Textstellen mit richtige %-StringOperatoren:"
for i in self.slice_pos_list(self.get_correct_pos()):
print i
doc = Find_StringOperators.__doc__
print doc
print "-"*80
s = Find_StringOperators(doc)
print "incorrect_pos:", s.get_incorrect_pos()
print "correct_pos:", s.get_correct_pos()
print "-"*80
s.debug_results()
Ausgaben:
Sucht in einem String nach %-StringOperatoren.
Dabei wird zwischen richtigen und falschen unterschieden.
Dient zur besseren Fehlerausgabe, bei String Operationen.
Test-Text:
----------
Hier ist ein Beispieltext mit %(richtigen)s Platzhaltern.
Aber auch mit %(falschen, da die hier Klammer nicht geschlossen ist.
Außerdem müßen einzelne %-Zeichen, immer escaped werden. Das wird
mit doppelten %% Zeichen gemacht, die nach dem String-Operation,
bei dem die %(Platzhalter)s durch Daten aus einem Dict ersetzt werden,
wieder zu einfachen %-Zeichen umgewandelt werden.
--------------------------------------------------------------------------------
incorrect_pos: [(11, 51), (267, 307), (353, 393), (563, 603)]
correct_pos: [(221, 261), (480, 520)]
--------------------------------------------------------------------------------
self.incorrect_hit_pos: [31, 287, 373, 583]
incorrect_pos: [(11, 51), (267, 307), (353, 393), (563, 603)]
>>> Textstellen mit falsche %-Zeichen im Text:
...in einem String nach %-StringOperatoren....
...n.\n Aber auch mit %(falschen, da die ...
...dem m\xc3\xbc\xc3\x9fen einzelne %-Zeichen, immer es...
... wieder zu einfachen %-Zeichen umgewande...
self.correct_hit_pos: [241, 500]
correct_pos: [(221, 261), (480, 520)]
>>> Textstellen mit richtige %-StringOperatoren:
...ein Beispieltext mit %(richtigen)s Platz...
...ion,\n bei dem die %(Platzhalter)s dur...
Verfasst: Freitag 15. September 2006, 17:58
von jens
Ich hab mir nun eine bessere Variante zusammen gebaut. Dabei wird einfach fehlende Keys im context eingefügt. So kann die Operation doch durchgeführt werden und gleichzeitig hat man eine Fehlermeldung:
Code: Alles auswählen
def fill_string_context(self, internal_page_name, content, context):
"""
Mach die Python-String-Operation. Wenn ein key im context fehlt, wird
der Fehler mit page_msg ausgegeben, gleichzeitig wird der fehlende Key
in den context eingefügt und nochmals probiert...
"""
try:
return content % context
except KeyError, key:
key = key[0]
self.page_msg(
"Key-Error: '%s' in internal page '%s'!" % (
key, internal_page_name
)
)
context[key] = "" # Fehlenden Key im context einfügen
# String-Operation nochmals versuchen:
content = self.fill_string_context(
internal_page_name, content, context
)
return content
self.page_msg() könnte auch sein: sys.stderr.write()
Verfasst: Freitag 15. September 2006, 18:47
von mitsuhiko
Warum nicht so?
Code: Alles auswählen
class PlaceholderMissmatch(Exception):
def __init__(self, required, found, diff):
self.required = required
self.found = found
self.diff = diff
Exception.__init__(self, 'required: %s; found: %s; diff: %s' % (
', '.join(required), ', '.join(found), ', '.join(diff)
))
def test(s, page_dict):
found = []
class FakeMap(object):
def __getitem__(self, name):
found.append(name)
return ''
s % FakeMap()
required = page_dict.keys()
if found != required:
diff = set(found).intersection(set(required))
raise PlaceholderMissmatch(required, found, sorted(diff))
test(
'Bla %(platzhalter1)s blup %(Platzhalter2)s...',
{
"platzhalter1": "jau",
"PPPaltzhalter": "Fehler"
}
)