Don't crash if feedparser.parse raises an exception.
authorAdam Sampson <ats@offog.org>
Sat, 1 Feb 2014 22:49:04 +0000 (22:49 +0000)
committerAdam Sampson <ats@offog.org>
Sat, 1 Feb 2014 22:49:04 +0000 (22:49 +0000)
Also add a testcase for this, and some explicit comments in the source
about where it has to handle incomplete responses.

NEWS
rawdoglib/rawdog.py
test-rawdog

diff --git a/NEWS b/NEWS
index 7be35f5d2b3a691fe585b82d9aa3a4e1046c7082..f37fffa8bd34dfffb0e58e06252e06d10b7d5047 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ with the state file.
 Add some more comprehensive tests for the changeconfig option; in
 particular, test it more thoroughly with splitstate both on and off.
 
+Don't crash if feedparser raises an exception during an update (i.e.
+assume that any part of feedparser's response might be missing, until
+we've checked that there wasn't an exception).
+
 - rawdog 2.18
 
 Be consistent about catching AttributeError when looking for attributes
index 3661df269c599acf46e8acbd18ffa9c94143627a..16b6f8b90abad8e9c8715d58175e9673c61b3e6c 100644 (file)
@@ -1,5 +1,5 @@
 # rawdog: RSS aggregator without delusions of grandeur.
-# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Adam Sampson <ats@offog.org>
+# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014 Adam Sampson <ats@offog.org>
 #
 # rawdog is free software; you can redistribute and/or modify it
 # under the terms of that license as published by the Free Software
@@ -457,10 +457,14 @@ class Feed:
                """Add new articles from a feed to the collection.
                Returns True if any articles were read, False otherwise."""
 
+               # Note that feedparser might have thrown an exception --
+               # so until we print the error message and return, we
+               # can't assume that p contains any particular field.
+
                responses = p.get("rawdog_responses")
                if len(responses) > 0:
                        last_status = responses[-1]["status"]
-               elif len(p["feed"]) != 0:
+               elif len(p.get("feed", [])) != 0:
                        # Some protocol other than HTTP -- assume it's OK,
                        # since we got some content.
                        last_status = 200
@@ -541,7 +545,7 @@ class Feed:
                        errors.append("If this condition persists, you should remove it from your config file.")
                        errors.append("")
                        fatal = True
-               elif version == "" and len(p["entries"]) == 0:
+               elif version == "" and len(p.get("entries", [])) == 0:
                        # feedparser couldn't detect the type of this feed or
                        # retrieve any entries from it.
                        errors.append("The data retrieved from this URL could not be understood as a feed.")
@@ -561,6 +565,9 @@ class Feed:
                        if fatal:
                                return False
 
+               # From here, we can assume that we've got a complete feedparser
+               # response.
+
                p = ensure_unicode(p, p.get("encoding") or "UTF-8")
 
                # No entries means the feed hasn't changed, but for some reason
index 2e86d7feac6176ccadb4dc7506353a273d7d086d..72c124e9d50579b8fc4ce1d32d2d03338ed2ae4c 100644 (file)
@@ -642,6 +642,18 @@ EOF
        done
 done
 
+begin "exception raised by feedparser"
+make_rss20 $statedir/feed.rss
+add "feed 0 feed.rss"
+cat >$statedir/plugins/crash.py <<EOF
+import feedparser
+def crash(*args, **kwargs):
+    raise Exception("exception from feedparser")
+feedparser.parse = crash
+EOF
+rune "Error fetching or parsing feed" -u
+contains $outfile "exception from feedparser"
+
 begin "with --no-locking"
 make_rss20 $statedir/simple.rss
 add "splitstate true"