atrapa errores dentro del generador y continúa después

Tengo un iterador que se supone que funciona durante varios días. Quiero que los errores se detecten e informen, y luego quiero que el iterador continúe. O todo el proceso puede comenzar de nuevo.

Aquí está la función:

def get_units(self, scraper): units = scraper.get_units() i = 0 while True: try: unit = units.next() except StopIteration: if i == 0: log.error("Scraper returned 0 units", {'scraper': scraper}) break except: traceback.print_exc() log.warning("Exception occurred in get_units", extra={'scraper': scraper, 'iteration': i}) else: yield unit i += 1 

Debido a que el scraper podría ser una de las muchas variantes de código, no se puede confiar en él y no quiero manejar los errores allí.

Pero cuando se produce un error en units.next() , todo se detiene. Sospecho porque un iterador lanza una StopIteration cuando una de sus iteraciones falla.

Aquí está la salida (sólo las últimas líneas)

     [2012-11-29 14:11:12 /home/amcat/amcat/scraping/scraper.py:135 DEBUG] Scraping unit  [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article [2012-11-29 14:11:13 /home/amcat/amcat/scraping/scraper.py:138 DEBUG] .. yields article Counter-Strike: Global Offensive Update Released Traceback (most recent call last): File "/home/amcat/amcat/scraping/controller.py", line 101, in get_units unit = units.next() File "/home/amcat/amcat/scraping/scraper.py", line 114, in get_units for unit in self._get_units(): File "/home/amcat/scraping/games/steamcommunity.py", line 90, in _get_units app_doc = self.getdoc(url,urlencode(form)) File "/home/amcat/amcat/scraping/scraper.py", line 231, in getdoc return self.opener.getdoc(url, encoding) File "/home/amcat/amcat/scraping/htmltools.py", line 54, in getdoc response = self.opener.open(url, encoding) File "/usr/lib/python2.7/urllib2.py", line 406, in open response = meth(req, response) File "/usr/lib/python2.7/urllib2.py", line 519, in http_response 'http', request, response, code, msg, hdrs) File "/usr/lib/python2.7/urllib2.py", line 444, in error return self._call_chain(*args) File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain result = func(*args) File "/usr/lib/python2.7/urllib2.py", line 527, in http_error_default raise HTTPError(req.get_full_url(), code, msg, hdrs, fp) HTTPError: HTTP Error 500: Internal Server Error [2012-11-29 14:11:14 /home/amcat/amcat/scraping/controller.py:110 WARNING] Exception occurred in get_units ...code ends... 

    Entonces, ¿cómo puedo evitar que la iteración se detenga cuando se produce un error?

    EDITAR: aquí está el código dentro de get_units ()

     def get_units(self): """ Split the scraping job into a number of 'units' that can be processed independently of each other. @return: a sequence of arbitrary objects to be passed to scrape_unit """ self._initialize() for unit in self._get_units(): yield unit 

    Y aquí hay un _get_units simplificado ():

     INDEX_URL = "http://www.steamcommunity.com" def _get_units(self): doc = self.getdoc(INDEX_URL) #returns a lxml.etree document for a in doc.cssselect("div.discussion a"): link = a.get('href') yield link 

    EDIT: seguimiento de preguntas: modifique cada bucle for en una función para que el manejo de errores se ejecute automáticamente después de cada iteración fallida

    StopIteration se StopIteration mediante el método next() de un generador cuando ya no hay ningún elemento siguiente. No tiene nada que ver con errores dentro del generador / iterador.

    Otra cosa a tener en cuenta es que, dependiendo del tipo de iterador, es posible que no pueda reanudarse después de una excepción. Si el iterador es un objeto con un método next , funcionará. Sin embargo, si en realidad es un generador, no lo hará.

    Por lo que puedo decir, esta es la única razón por la que su iteración no continúa después de un error de units.next() . Es decir, units.next() falla, y la próxima vez que lo llame, no puede reanudar y dice que se hizo lanzando una excepción StopIteration .

    Básicamente, tendría que mostrarnos el código dentro de scraper.get_units() para que entendamos por qué el bucle no puede continuar después de un error dentro de una única iteración. Si get_units() se implementa como una función de generador, está claro. Si no, puede ser otra cosa que impida que se reanude.

    ACTUALIZACIÓN: explicando qué es una función de generador:

     class Scraper(object): def get_units(self): for i in some_stuff: bla = do_some_processing() bla *= 2 # random stuff yield bla 

    Ahora, cuando llama a Scraper().get_units() , en lugar de ejecutar toda la función, devuelve un objeto generador. Llamando a next() en él, llevará la ejecución al primer yield . Etc. Ahora, si se produce un error EN CUALQUIER LUGAR dentro de get_units , se get_units , por así decirlo, y la próxima vez que llame a next() , se generará StopIteration , como si se hubiera quedado sin elementos para darle.

    Se recomienda leer http://www.dabeaz.com/generators/ (y http://www.dabeaz.com/coroutines/ ).

    ACTUALIZACIÓN2: Una posible solución https://gist.github.com/4175802