String-Formatter finden...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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 :(

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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() :)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

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"
    }
)
TUFKAB – the user formerly known as blackbird
Antworten