doctest und yield

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
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

Hi,

ich habe gerade ein Problem mit doctests. Ich definiere eine Funktion, die einen Generator zurückliefern soll.

Code: Alles auswählen

import os

def scanfolder(dst):
    """scan the given folder, return a list of all files

    dst must be a valid folder
        >>> scanfolder('a_folder_that_doesn_t_exist')
        Traceback (most recent call last):
            ...
        IOError: the specified folder does not exist

    return a generator
        >>> g = scanfolder("/home")
        >>> print g
        <generator object ...>
    """
    if not os.path.isdir(dst):
        raise IOError, "the specified folder does not exist"

    for root, dirs, files in os.walk(dst):
        for file in files:
            yield os.path.join(root, file)


if __name__ == '__main__':
    import doctest
    doctest.testmod(optionflags=doctest.ELLIPSIS)
Nun schlägt der eine Test aber immer wieder fehl, obwohl eine Exception kommen müsste wird ein Generator geliefert.
Failed example:
scanfolder('a_folder_that_doesn_t_exist')
Expected:
Traceback (most recent call last):
...
IOError: the specified folder does not exist
Got:
<generator object at 0xb7a56f2c>
**********************************************************************
1 items had failures:
1 of 3 in __main__.scanfolder
***Test Failed*** 1 failures.
Mache ich was falsch oder kann man sowas mit doctests nicht richtig prüfen?
BlackJack

Es liegt nicht am Test sondern am Code. Vielleicht solltest Du Doctest einfach mal glauben:

Code: Alles auswählen

Python 2.4.4c1 (#2, Oct 11 2006, 21:51:02)
[GCC 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from test import *
>>> scanfolder('a_folder_that_doesn_t_exist')
<generator object at 0xb7d0712c>
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Vielleicht hilft dir das weiter:

Code: Alles auswählen

def g():
    1/0
    yield None

x = g()
print "Generator erstellt"
x.next()
print "next() aufgerufen"
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

Nicht wirklich. Soll das heißen es wird immer ein Generator geliefert, egal was ich vorher mache?
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Nicht wirklich. Soll das heißen es wird immer ein Generator geliefert, egal was ich vorher mache?
Japp. `yield` liefert immer ein generatorobjekt zurück. Nur die Werte des Objektes können verschieden sein. Es kann imprinziep alles(?) sein. Von Exceptions über None, bis hin zu normalen Werten wie Listen und Strings.

Code: Alles auswählen

In [10]: def g():
    yield [1, 2, 3, 5, 6]
    yield 1
    yield (5, 'la')
    raise Error("Einfach mal nen Fehler?")
    yield "und weiter gehts"
   ....: 

In [16]: generator = g()

In [17]: generator.next()
Out[17]: [1, 2, 3, 5, 6]

In [18]: generator.next()
Out[18]: 1

In [19]: generator.next()
Out[19]: (5, 'la')

In [20]: generator.next()
---------------------------------------------------------------------------
<class '__main__.Error'>                  Traceback (most recent call last)

/home/ente/<ipython console> in <module>()

/home/ente/<ipython console> in g()

<class '__main__.Error'>: Einfach mal nen Fehler?
Jedoch gehts nach einem Fehler nicht weiter. d.H, an den code ``""und weiter gehts"`` kommst du nicht mehr ran.

MfG EnTeQuAk

€dit: Den Beispielcode verbessert ;)
Zuletzt geändert von EnTeQuAk am Freitag 18. Mai 2007, 17:57, insgesamt 3-mal geändert.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

TheGrudge hat geschrieben:Nicht wirklich. Soll das heißen es wird immer ein Generator geliefert, egal was ich vorher mache?
Ganz richtig. Der Code im Generator wird erst bei ersten Aufruf von `next()` von Anfang an ausgeführt.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

TheGrudge hat geschrieben:Nicht wirklich. Soll das heißen es wird immer ein Generator geliefert, egal was ich vorher mache?
jein, das soll heissen, dass der Generator geliefert wird, der Code aber erst ausgeführt wird, wenn du Elemente aus dem Generator holst, und auch nur so weit, wie Elemente rausgeholt werden.

Dein Generator wird die exception beim ersten "next()" werfen.

Wenn du schon bei der erstellung die exception raisen willst, könntest du eine Funktion schreiben, die deinen generator zurückgibt.

Code: Alles auswählen

def g(arg):
  if not check(arg):
    raise Exception
  else:
    return _g(arg)

def _g(arg):
   ...
   yield something

Benutzeravatar
TheGrudge
User
Beiträge: 96
Registriert: Donnerstag 4. Mai 2006, 18:39

OK, danke an alle, mal wieder was gelernt!!!
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

keppla hat geschrieben:

Code: Alles auswählen

def g(arg):
  if not check(arg):
    raise Exception
  else:
    return _g(arg)

def _g(arg):
   ...
   yield something
Etwas hässlich. Dafür sind innere Funktionen hervorragend geeignet:

Code: Alles auswählen

def g(arg):
    if not check(arg):
        raise Exception
    else:
        def g(arg):
             yield ...
        return g(arg)
[/code]
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Antworten