Rekursion: maximum recursion depth exceeded

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
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Hallo zusammen,

ich habe ein Rekursionsproblem da angeblich die maximal Rekursionstiefe erreicht wurde.

Fehlermeldung: "maximum recursion depth exceeded"

Dachte, ah ok hast halt einen Denkfehler eingebaut.

Aber nach etwas suchen ist mir in meinem Code nichts aufgefallen und so habe ich mit einer "statischen " Variablen (ja die gibts nicht) einfach mal Methodenaufrufe mitgezählt.
Angeblich ist bei 10 Aufrufen Schluss, ok dacht ich mir. Das kann nicht sein, Standard ist ja meistens 2000.

googel googel.

sys.getrecursionlimit(), setrecursionlimit(limit)

Mit diesen Funktionen kann die maximale Rekursionstiefe ausgelesen oder verändert werden. Die maximale Rekursionstiefe ist mit 1000 vorbelegt und bricht endlos rekursive Funktionsaufrufe ab, bevor diese zu einem Speicherüberlauf führen können.
Default Rekursionstiefe bei 1000. Also gehe ich davon aus, er erkennt frühzeitig das es "endlos" ist.

Da wollt ich fragen wie er das erkennt?


Grüße

anbei der code, falls interessant

Code: Alles auswählen

def build(self):
        try:
            self.htmlDoc = HTML("<html><head></head><body></body></html>") 
            self.__recursiveListHandling([], self.orderFields, self.whereFields)
            print self.htmlDoc
        except Exception, e:
            print e
        pass
    

    #===========================================================================
    def __recursiveListHandling(self,usedList,toUseList, whereFields):
        try:
            if len(toUseList) > 0:
                ....
                for result in fieldList:
                    ....
                    self.__recursiveListHandling(copy.deepcopy(usedList),
                                                  copy.deepcopy(copyToUseList), 
                                                  copy.deepcopy(copyWhereFields))
            else:
                ....
                    self.__writeTicketBlock(ticket[0])
            pass
        except Exception, e:
           ...
    
    
 
    def __writeTicketBlock(self,ticketId):
        for i in range(0,6)):
            try:
               ...
                self.htmlDoc = self.htmlDoc | Transformer('.//body').append(" &nbsp %s \n"%sqlResult[0][i])
               ...

            except Exception, e:
                print e
                break
        pass
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

rads hat geschrieben:Aber nach etwas suchen ist mir in meinem Code nichts aufgefallen und so habe ich mit einer "statischen " Variablen (ja die gibts nicht) einfach mal Methodenaufrufe mitgezählt.
Angeblich ist bei 10 Aufrufen Schluss, ok dacht ich mir.
Sicher dass du richtig gezählt hast?
Da wollt ich fragen wie er das erkennt?
Unendlich bedeutet > sys.getrecursionlimit()
BlackJack

@rads: Wie sieht denn der Traceback aus? Da müsstest Du doch sehen was die Aufrufe sind, und auch das es wohl mehr als 10 sind.

Der Code sieht ein wenig komisch aus. Was sollen die ganzen Fehlerbehandlungen die eigentlich keine sind? Ausgabe jeder beliebigen Ausnahme ist keine adäquate Fehlerbehandlung.

`deepcopy()` ist IMHO auch ein "code smell". Das braucht man nur ganz selten. Kann es vielleicht sogar sein, dass Du da irgendwie eine rekursive Datenstruktur gebaut hast, und die eventuell Probleme beim Kopieren verursacht?

Die doppelten führenden Unterstriche solltest Du durch einzelne ersetzen. Das ist in dem Sinne kein "private" und sollte auch nicht als solches missbraucht werden.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Schonmal danek für die schnelle Antwort.

Sicher natürlich nicht, da es ja keine statische Variable ist

ich habe in build eine variable mit self.counter = 0 angelegt
und in den Methoden recursiveListHandling und writeTicketBlock
mache ich als ersten Befehl self.counter = self.counter + 1

Also an sich sogar mehr als die eigentliche Rekurssionstiefe.

sys.getrecursionlimit() liefert mir 1000 zurück.

Ok, dann MUSS ich einen Fehler gemacht habe, doch
etwas schleierhaft ist mir das schon :)

@Blackjack
- Traceback gibts nicht da ich die Exception fange, Meldung ist wie oben geschrieben max recursion..
- Fehlerbehandlungen sind rein als ich den aktuellen Fehler gesucht habe. Ich bekomme sonst kein Traceback da
Trac das verschluckt (schreibe ein Plugin für Trac)
- Dachte auch erst an deepcopy, aber bei kleinern Mengen macht er die Rekursion fehlerfrei, also ist hier nichts "endloses" drinnen. Aber ich sehe mir das nochmal genauer an
- Ok ändere das mit private ab, dann bedeute für mich private etwas anderes :)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

rads hat geschrieben: - Traceback gibts nicht da ich die Exception fange, Meldung ist wie oben geschrieben max recursion..
Ja, BlackJack meinte dass man das eben nicht machen sollte.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Leonidas hat geschrieben:
rads hat geschrieben: - Traceback gibts nicht da ich die Exception fange, Meldung ist wie oben geschrieben max recursion..
Ja, BlackJack meinte dass man das eben nicht machen sollte.
rads hat geschrieben:- Fehlerbehandlungen sind rein als ich den aktuellen Fehler gesucht habe. Ich bekomme sonst kein Traceback da
Trac das verschluckt (schreibe ein Plugin für Trac)
BlackJack

@rads: Wird das wirklich verschluckt? Wie soll man denn da Plugins entwickeln? Wenn das nicht schon im Log landet, gibt es sicher eine Möglichkeit Trac so zu konfigurieren, *dass* es im Log landet.

Ansonsten musst Du den Stacktrace halt selbst ausgeben.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

@Blackjack

Grundsätzlich schon, leider frisst sich bei einer Recursion-Exception der Thread.

Trac bekommt nichts von einem Fehler mit, Oberfläche ist weiterhin möglich, allerdings
hat sich mein Modull komplett verabschiedet und ist nicht mehr ansprechbar, was aber
Trac auch nicht mitbekommt.

Deswegen mache ich es leider so. Danke Blackjack, das mit den print stacktrace hilft
mir hoffentlich, bis jetzt habe ich noch nicht rausgefunden wo das problem ist, tippe aber
das es mit den verarbeiten der Listen und der kopieren irgendwie zusammen hängt.

Grüße

Stefan
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Fehler jetzt eingeschränkt,

Exception: maximum recursion depth exceeded
Befehl: print self.htmlDoc

Definition: self.htmlDoc = HTML("<html><head></head><body></body></html>")
Operationen: self.htmlDoc = self.htmlDoc | Transformer('.//body') \
.append(Element('h%s'%(len(usedList)+1))(tag.p()("[%s] %s: "%(ticket,ticketSummary))))

An sich reiche ich das self object immer durch und führe darauf ausschließlich Transformationen mit Genshi aus.
Gibt es möglichkeiten die Struktur einer Variablen zu analysieren? debuggen ist leider auch nicht.

Falls ich was finde geb ich euch bescheid :) Ein Kaffee und ein Bier das es ein idiotischer Fehler ist.
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Ok,

offenbar wird jedesmal, wenn ich self.html = self.html | Transform (...) eine Rekursion aufgerufen.
Wenn ich die Anzahl deutlich reduziere, läuft der Code bekanntlich fehlerfrei.

mal schaun wo hier der denkfehler ist

Anbei ein Minimalbeispiel:

Code: Alles auswählen

    def build(self):
        self.htmlDoc = HTML("<html><head></head><body></body></html>") 
        self.addNumber()
        print self.htmlDoc

    
    
    def addNumber(self):
        for number in range(0,5000):
            self.htmlDoc = self.htmlDoc |Transformer('.//body').append(tag.p()("number %s"%number))
Habe meinen traceback gefunden (ist nur ein ausschnitt, aber immer das gleiche)

Code: Alles auswählen

#   File "C:\Entwicklungsecke\Software\Python26\lib\site-packages\genshi\core.py", line 267, in  _ensure
Code fragment:

 262. COMMENT = Stream.COMMENT
 263.  
 264. def _ensure(stream):
 265. """Ensure that every item on the stream is actually a markup event."""
 266. stream = iter(stream)
 267. event = stream.next()
 268.  
 269. # Check whether the iterable is a real markup event stream by examining the
 270. # first item it yields; if it's not we'll need to do some conversion
 271. if type(event) is not tuple or len(event) != 3:
 272. for event in chain([event], stream):

Local variables:
Name	Value
stream 	<generator object _unmark at 0x0329BDF0>
# File "C:\Entwicklungsecke\Software\Python26\lib\site-packages\genshi\filters\transform.py", line 686, in _unmark
Code fragment:

 681. def _mark(self, stream):
 682. for event in stream:
 683. yield OUTSIDE, event
 684.  
 685. def _unmark(self, stream):
 686. for mark, event in stream:
 687. kind = event[0]
 688. if not (kind is None or kind is ATTR or kind is BREAK):
 689. yield event
 690.  
 691.  

Local variables:
Name	Value
self 	<genshi.filters.transform.Transformer object at 0x032457F0>
stream 	<generator object __call__ at 0x0329BDC8>
# File "C:\Entwicklungsecke\Software\Python26\lib\site-packages\genshi\filters\transform.py", line 1144, in __call__
Code fragment:

1139. def __call__(self, stream):
1140. """Apply the transform filter to the marked stream.
1141.  
1142. :param stream: The marked event stream to filter
1143. """
1144. for mark, event in stream:
1145. yield mark, event
1146. if mark is ENTER:
1147. for mark, event in stream:
1148. if mark is EXIT:
1149. break

Local variables:
Name	Value
self 	<genshi.filters.transform.AppendTransformation object at 0x03245870>
stream 	<generator object __call__ at 0x0329BDA0>
# File "C:\Entwicklungsecke\Software\Python26\lib\site-packages\genshi\filters\transform.py", line 713, in __call__
Code fragment:

 708. """
 709. namespaces = {}
 710. variables = {}
 711. test = self.path.test()
 712. stream = iter(stream)
 713. for mark, event in stream:
 714. if mark is None:
 715. yield mark, event
 716. continue
 717. result = test(event, {}, {})
 718. # XXX This is effectively genshi.core._ensure() for transform

Local variables:
Name	Value
namespaces 	{}
self 	<genshi.filters.transform.SelectTransformation object at 0x032457B0>
stream 	<generator object _mark at 0x0329BD78>
test 	<function _test at 0x033455B0>
variables 	{}
# File "C:\Entwicklungsecke\Software\Python26\lib\site-packages\genshi\filters\transform.py", line 682, in _mark 
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

Lösung:

Ok, gefällt mir nicht, ist aber wahrscheinlich schneller

Ich cache den Text in einer String Variablen
und mache nur eine Transformation mit Genshi.

Merke: Streamtransformationen mit Genshi über 2000x aufrufen ist nix gut.

Beispielslösung:

Code: Alles auswählen

test = ""
        for number in range(0,5000):
            test = "%s %s"%(test,tag.strong()(number))
        self.htmlDoc = self.htmlDoc | Transformer(".//body").append(HTML(test))
BlackJack

@rads: Das ist IMHO eine sehr eigenartige Methode um so eine Zeichenkette zusammenzusetzen. Die idiomatische und auch effizientere Variante sieht so aus:

Code: Alles auswählen

        test = ' '.join(str(tag.strong()(n)) for n in xrange(5000))
rads
User
Beiträge: 153
Registriert: Freitag 26. März 2010, 15:51

@BlackJack Danke, war nur ein Beispiel um die Methode mal schenll 5000x aufzurufen. Ansonsten läuft das ja über meine rekursive methode wie zu Begin gepostet. Aber danke BlackJack, ich freue mich immer über Anregungen deinerseits ;)
Antworten