doctest und dict (Key Reihenfolge)

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:

Dumm eigentlich das DocTest die Reihenfolge eines dicts wichtig ist :(

Bsp:

Code: Alles auswählen

def test(d):
    """
    Das geht:
    >>> test({"A":1, "B":2, "C":3})
    {'A': 1, 'C': 3, 'B': 2}
    
    Im prinzip das selbe, aber fehler:
    >>> test({"A":1, "B":2, "C":3})
    {'A': 1, 'B': 2, 'C': 3}
    
    kommt:
    Failed example:
        test({"A":1, "B":2, "C":3})
    Expected:
        {'A': 1, 'B': 2, 'C': 3}
    Got:
        {'A': 1, 'C': 3, 'B': 2}
    """
    return d
Ist doch eigentlich unschön, was?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Naja ... probier doch lieber folgendes mal ;)

Code: Alles auswählen

"""
>>> expected = {'A': 1, 'C': 3, 'B': 2}
>>> got = test({'A': 1, 'C': 3, 'B': 2})
>>> expected == got
True
"""
Gleiches Verfahren lässt bei allen anderen Containern/Klassen relativ gut anwenden, die ebenfalls keine Ordnung besitzen.

>>Masaru<<
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie meinst du das? Das ich den DocTest so schreiben sollte:

Code: Alles auswählen

def test(d):
    """
    >>> test({"A":1, "B":2, "C":3}) == {'A': 1, 'B': 2, 'C': 3}
    True
    """
    return d


if __name__ == "__main__":
    import doctest
    doctest.testmod()
IMHO zu umständlich.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Code: Alles auswählen

def test(d):
    """
    >>> expected = {'A': 1, 'C': 3, 'B': 2}
    >>> got = test({'A': 1, 'C': 3, 'B': 2})
    >>> expected == got
    True
    """
    return d
   

if __name__ == '__main__':
   import doctest
   doctest.testmod()
.. oder Deine Variante ... wie auch immer, eine andere Möglichkeit hast Du bei ungeordneten Datentypen einfach nicht.

Und wenn Dir "das" schon zu umständlich ist, dann will ich erst gar nicht wissen, wie Deine UnitTests aussehen .... Du schreibst doch welche, oder?

>>Masaru<<
Zuletzt geändert von Masaru am Donnerstag 10. Juli 2008, 14:03, insgesamt 1-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Naja, das ist ja noch mehr code ;)
Bei beiden Varianten ist außerdem doof, das man bei einem Fehler das sieht:

Code: Alles auswählen

Expected:
    True
Got:
    False
Das hilft weniger weiter...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

*g* Du kannst aber auch ein unkreativer Geist sein ;)

Probier das hier mal:

Code: Alles auswählen

def test(d):
    """
    >>> expected = {'A': 1, 'C': 3, 'B': 2}
    >>> got = test({'A': 1, 'C': 3, 'B': 2})
    >>> if expected != got:
    ...    assert False, "%s != %s" % (expected, got)
    """
    return d
   

if __name__ == '__main__':
   import doctest
   doctest.testmod()
Optional kannst du Zeile 5,6 natürlich auch gegen das hier austauschen um eine Zeile zu sparen:

Code: Alles auswählen

>>> assert expected == got, "%s != %s" % (expected, got)
Wenn 'expected != got' (z.B. bei 'return {}', kommt etwas in dieser Art raus:

Code: Alles auswählen

*****************************************************************
Failure in example:
if expected != got:
   assert False, "%s != %s" % (expected, got)
from line #3 of __main__.test
Exception raised:
Traceback (most recent call last):
  File "C:\_PYTHON_\python23\lib\doctest.py", line 442, in _run_examples_inner
    compileflags, 1) in globs
  File "<string>", line 2, in ?
AssertionError: {'A': 1, 'C': 3, 'B': 2} != {}
*****************************************************************
Na, hilft Dir das mehr?

---

Und um auf Deine ursprüngliche Frage zurückzukommen:
jens hat geschrieben:Dumm eigentlich das DocTest die Reihenfolge eines dicts wichtig ist :(
...
Ist doch eigentlich unschön, was?
Joa, "eigentlich" praktisch gesehen schon.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Naja, klar könnte man noch mehr code in die DocTest packen, damit es besser funktioniert. Das ist aber IMHO nicht Sinn der Sache. Damit wären die DocTest aufgebläht und undurchsichtig.

IMHO sollte das eigentlich Problem an der Wurzel angepackt werden. Man müsste das direkt irgendwo im DocTest Modul lösen...

Offensichtlich wird nur ein sowas ähnliches gemacht: repr(got) == repr(expected)

Irgendwo bei OutputChecker.check_output() müßte man etwas ändern: http://svn.python.org/view/python/trunk ... iew=markup

Im Bugtracker kann ich dazu nichts finden: http://bugs.python.org/issue?%40columns ... pen+issues

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Leider vollzieht check_output() eine String-Prüfung: got == want (wobei got und want beides Strings sind)

Man müsste erstmal die Strings in Python-Objekte konvertieren, z.B. mit eval():

Code: Alles auswählen

...
       if eval(got) == eval(want):
            return True
...
Wobei dann die Streiterei "eval is evil" und "dann müssen wir aber Syntax-Checks vorrangestellt einbauen" losgeht ;).

@Edit:
Mhm .. ausserdem würde es ärger machen, wenn "want" und "got" tatsächlich nur strings sein sollen *g*.
Tjoa ... ein wenig mehr drumrum muss also schon noch gebaut werden.

@Edit2:
Mhmm ... wobei wenn man das Methoden-Ende wie folgt anpasst, könnte es funktionieren:

Code: Alles auswählen

...
        #-----<add>-----
        try:
            if eval(got) == eval(want):
                return True
        except:
            pass #*pfeif* kein schoener stil, aber pragmatisch
        #-----</add>----
        # We didn't find any match; return false.
        return False
>>Masaru<<
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Man könnte auch eval() nur dann anwenden, wenn das erste und letzte Zeichen "{" und "}" ist... Aber auch das ist unschön...

Oder man nutzt eine Alternative zu eval() siehe auch: http://www.python-forum.de/topic-14444.html

Zum Beispiel data_eval: http://trac.pylucid.net/browser/trunk/p ... ta_eval.py

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:

Hab mal eine Bug Meldung gemacht: http://bugs.python.org/msg69501

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

*hihi* mach was Du willst ... ich habe testweise es mal mit meiner Variante 'eval am Schluss nochmal zu verwenden' probiert: mit Erfolg :)

Ob nun und das standard eval ... oder eine 'secure' Variaten: letztendlich sind DocTests Werkzeuge/Verfahren, die während der "Entwicklung" im Rahmen der Softwareentwicklung zur Steigerung der Produkt/Projektqualität eingesetzt werden.

Wenn jemand dieses Risiko akzeptiert, kann er sein DocTest Modul umschreiben ... wenn nicht, hat er weiterhin die Möglichkeit durch ausführlichere DocTests das gewünschte Ziel ebenfalls zu erreichen.

@hustet: die Reaktion auf Dein Bug-Issue war übrigens vorhersehbar ;) ... aber mach Dir keinen Kopf *g*, für Dich kannst Du es ja anpassen wie Du möchtest.
Denn alles andere steht ja bereits hier: http://docs.python.org/lib/doctest-warnings.html (somit haben sich die Python-Entwickler schonmal wenigstens gut abgesichert)

>>Masaru<<
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Die Reaktion finde ich allerdings ernüchternd. Es wird IMHO der einfachste Weg gegangen. Gut, es ist kein Bug, von mir aus ist es dann ein "Feature Request"...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

Eine Reaktion, wie sie aus einer elitären Gruppe immer gegeben wird, wenn es um Innovation - angestossen durch einen der nicht zum inneren Kreis gehört - geht.

Genauso wird hier ja auch im Forum verfahren ;).

--
Was die Entscheidung angeht Dein Bug-Issue zu closen ... mit der Entscheidung muss man nun leben.

Ich denke mal wenn das Issue von einem Christian Heimes oder Georg Bandl gekommen wären, hätte man dem mehr Augenmerk geschenkt und eine Chance zur Diskussion entgegengebracht.

Kopf hoch ... kannst es ja in einem halben Jahr nochmal probieren :D.

>>Masaru<<
BlackJack

Ich denke nicht, dass das etwas mit elitärem Gehabe zu tun hat, sondern ganz einfach damit, dass sich Doctests genau wie der Interpreter verhalten sollten. Je mehr man daran herum doktort um so grösser ist die Gefahr, dass sich das Verhalten unterscheidet und damit die Tests nicht mehr aussagekräftig sind. Wenn etwas wie die Reihenfolge von Dictionary-Einträgen in Produktionscode nicht garantiert ist, dann sollte das bei einem Test nicht plötzlich der Fall sein.

Gegen `eval()` dürfte sprechen, dass der "Rückgabewert" gar kein gültiges Python sein muss. Oder umgekehrt: es ist zufälliges Python, das laut `eval()` äquivalent ist, es aber eigentlich gar nicht sein dürfte.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

BlackJack hat geschrieben:Gegen `eval()` dürfte sprechen, dass der "Rückgabewert" gar kein gültiges Python sein muss. Oder umgekehrt: es ist zufälliges Python, das laut `eval()` äquivalent ist, es aber eigentlich gar nicht sein dürfte.
Ich weiß jetzt nicht so 100%tig was du damit meinst.
Meinst du sowas:

Code: Alles auswählen

def test1():
    """
    Es kommt ein String zurück:
    >>> test1()
    '{123}'

    Kein dict, deswegen schlägt das fehl, kein gültiges Python dict:
    >>> test1()
    {123}
    """
    return "{123}"
Also das die Fuktion nur sowas ähnliches die ein dict zurück liefert?

Allerdings ist es doch so: DocTest ruft doch die funktion auf und übergibt die Argumente und erhält das Ergebnis. Somit liegt zumindest das Ergebnis in dem richtigen Format vor. Da kann man auf eval() ganz verzichten.
Man könnte also sowas machen: Ist der Ergebnis (got) ein dict und expected fängt mit "{" and und hört mit "}" auf, muß expected wohl ein dict sein, eval() wird angewendet...

EDIT: Achja, doctest vergleicht ja auch Ausgaben der Funktionen. Wenn also ein dict mit print Ausgegeben wird, kommt es zu Problemen...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten