3 Module for polling the feeds.
5 __copyright__
= "Copyright (c) 2002-4 Juri Pakaste <juri@iki.fi>"
6 __license__
= """ GNU General Public License
8 Straw is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
13 Straw is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
23 from MainloopManager
import MainloopManager
24 import NetworkConstants
31 from MessageManager
import post_status_message
32 from straw
import helpers
34 class PollStopper(gobject
.GObject
):
37 'stopped' : (gobject
.SIGNAL_RUN_LAST
, gobject
.TYPE_NONE
, ())
40 def __init__(self
, stopper
, object):
41 gobject
.GObject
.__init
__(self
)
42 self
._stopper
= stopper
45 stopper
= property(lambda self
: self
._stopper
)
46 object = property(lambda self
: self
._object
)
49 if self
._stopper
is not None:
52 self
.emit_signal('stopped')
56 config
= Config
.get_instance()
57 self
._feedlist
= feeds
.feedlist
59 config
.connect('refresh-changed', self
.poll_changed
)
60 config
.connect('offline-mode-changed', self
.offline_changed
)
62 self
._feedlist
.connect('changed', self
.feeds_changed
)
64 self
._is
_offline
= config
.offline
65 self
._poll
_frequency
= config
.poll_frequency
66 self
._last
_poll
= config
.last_poll
67 self
._feeds
= self
._feedlist
.flatten_list()
70 def poll_changed(self
, config
):
71 """ called when global poll frequency has changed"""
72 self
._poll
_frequency
= config
.poll_frequency
74 def offline_changed(self
, config
):
75 self
._is
_offline
= config
.offline
77 def feeds_changed(self
, *args
):
78 self
._feedlist
= feeds
.feedlist
79 self
._feeds
= self
._feedlist
.flatten_list()
81 def start_polling_loop(self
):
82 mlmgr
= MainloopManager
.get_instance()
83 mlmgr
.set_repeating_timer(NetworkConstants
.POLL_INTERVAL
, self
.maybe_poll
)
85 ##########################
86 # Why both pollcontext and self._pollers?
87 # self._pollers is there to make sure that there are no simultaneous
88 # pollers on the same object. So the user doesn't accidentally start
89 # a poller for an object being polled.
90 # pollcontext is there so that we can avoid starting pollers many
91 # times - even though they wouldn't overlap - on the same object within
94 # because maybe_poll is used as an idle handler, it must always return
95 # True or else it's removed from the handler list
99 def _maybe_poll(self
):
100 now
= int(time
.time())
105 config_pf
= self
._poll
_frequency
106 feed_use_default
= feeds
.Feed
.DEFAULT
107 self
.poll([feed
for feed
, timediff
, fpf
in
108 [(feed
, now
- feed
.last_poll
, feed
.poll_frequency
)
109 for feed
in self
._feeds
]
110 if ((timediff
> fpf
> 0) or
111 (fpf
== feed_use_default
and
112 timediff
> config_pf
> 0))], context
)
115 self
.poll_categories([cat
for cat
, sub
, timediff
in
116 [(cat
, cat
.subscription
, now
- cat
.subscription
.last_poll
)
117 for cat
in feeds
.category_list
.all_categories
118 if cat
.subscription
is not None]
119 if ((timediff
> sub
.frequency
> 0) or
120 (sub
.frequency
== sub
.REFRESH_DEFAULT
and
121 timediff
> config_pf
> 0))], context
, False)
123 error
.log_exc("Caught an exception while polling")
126 def poll(self
, obj
, pollcontext
= None):
127 """ obj must be a list of Feed objects"""
128 if pollcontext
is None:
133 if not self
._pollers
.has_key(f
) and not pollcontext
.has_key(f
):
134 self
._pollers
[f
] = True
135 pollcontext
[f
] = True
136 sf
= FeedPoller(f
).poll()
140 def poll_categories(self
, cats
, pollcontext
= None, feeds
= True):
141 """Polls categories and, if feeds is True, the feeds
142 associated with them. Returns a list of list containing
143 PollStopper objects. poll_categories([c1, c2, c3]) -> [[sc1,
144 sc1f1, sc1f2 ...], [sc2, sc2f1, sc2f2 ...], ...] where cN is a
145 category, scN is a PollStopper for cN, scNfM is a stopper for
146 a feed in cN. If no polling was started for that cN or cNfM,
147 in its place is None. """
149 if pollcontext
is None:
152 category_stoppers
= []
154 if c
.subscription
is not None and not self
._pollers
.has_key(c
) and not pollcontext
.has_key(c
):
155 self
._pollers
[c
] = True
156 pollcontext
[c
] = True
157 sc
= CategoryPoller(c
, pollcontext
).poll()
158 category_stoppers
.append(sc
)
162 if not self
._pollers
.has_key(f
) and not pollcontext
.has_key(f
):
163 self
._pollers
[f
] = True
164 pollcontext
[f
] = True
165 sf
= FeedPoller(f
).poll(), f
166 category_stoppers
.append(sf
)
167 stoppers
.append(category_stoppers
)
170 def poll_done(self
, obj
):
171 if self
._pollers
.has_key(obj
):
172 del self
._pollers
[obj
]
175 def __init__(self
, feed
):
179 MainloopManager
.get_instance().call_pending()
181 url
, user
, pw
= self
._feed
.access_info
183 config
= Config
.get_instance()
184 self
._feed
.last_poll
= int (time
.time())
188 stopper
= URLFetch
.get_instance().request(
189 url
, self
, user
, pw
, priority
=NetworkConstants
.PRIORITY_RSS
)
190 ps
= PollStopper(stopper
, self
._feed
)
192 error
.log_exc("Caught an exception while polling")
195 self
._feed
.router
.start_polling()
196 self
._feed
.router
.stopper
= ps
200 def http_results(self
, status
, data
):
201 MainloopManager
.get_instance().call_pending()
208 #elif status[1] == 304:
209 # self._feed.router.route_no_data()
211 #elif status[1] == 410 or status[1] == 404:
212 # err = _("Unable to find the feed (%s: %s)") % (
213 # status[1], status[2].strip())
214 #elif status[1] == 401:
215 # err = _("Invalid username and password.")
216 #elif status[1] > 299:
217 # err = _("Updating feed resulted in abnormal status '%s' (code %d)") % (
218 # status[2].strip(), status[1])
220 (x
, code
, message
) = status
222 self
._feed
.router
.route_no_data()
228 self
._feed
.router
.set_error(err
)
231 parsed
= SummaryParser
.parse(data
, self
._feed
)
233 error
.log_exc("exception in summaryparser")
234 self
._feed
.router
.set_error(
235 _("An error occurred while processing feed: %s") %
238 self
._feed
.router
.route_all(parsed
)
239 post_status_message(_("Updating %s done.") % self
._feed
.title
)
240 except Exception, ex
:
241 error
.log_exc("error while parsing results")
242 self
._feed
.router
.set_error(str(ex
))
244 get_instance().poll_done(self
._feed
)
246 def http_failed(self
, exception
):
248 if isinstance(exception
, socket
.error
):
249 self
._feed
.router
.route_no_data()
251 self
._feed
.router
.set_error(str(exception
))
252 post_status_message(_("Updating %s failed") % self
._feed
.title
)
253 self
._feed
.poll_done()
255 get_instance().poll_done(self
._feed
)
257 def http_permanent_redirect(self
, location
):
259 (oldloc
, u
, p
) = self
._feed
.access_info
260 self
._feed
.access_info
= (location
, u
, p
)
262 def operation_stopped(self
):
263 self
._feed
.router
.route_no_data()
264 self
._feed
.poll_done()
265 get_instance().poll_done(self
._feed
)
267 class CategoryPoller
:
268 def __init__(self
, category
, pollcontext
):
269 self
._category
= category
270 self
._pollcontext
= pollcontext
273 sub
= self
._category
.subscription
274 if sub
is None or sub
.location
is None or len(sub
.location
) == 0:
276 sub
.last_poll
= int(time
.time())
280 stopper
= URLFetch
.get_instance().request(
281 sub
.location
, self
, sub
.username
, sub
.password
, priority
=NetworkConstants
.PRIORITY_RSS
)
282 ps
= PollStopper(stopper
, self
._category
)
284 error
.log_exc("Caught an exception while polling category")
288 def http_results(self
, status
, data
):
289 sub
= self
._category
.subscription
293 (x
, code
, message
) = status
295 self
._feed
.router
.route_no_data()
301 self
._category
.subscription
.error
= err
304 old_feeds
= self
._category
.feeds
305 self
._category
.read_contents_from_subscription()
306 common
, inold
, innew
= helpers
.listdiff(
307 old_feeds
, self
._category
.feeds
)
309 get_instance().poll(innew
, self
._pollcontext
)
311 self
._category
.subscription
.error
= str(e
)
313 get_instance().poll_done(self
._category
)
315 def http_failed(self
, exception
):
316 self
._category
.subscription
.error
= str(exception
)
317 get_instance().poll_done(self
._category
)
319 def http_permanent_redirect(self
, location
):
320 error
.logparam(locals(), "location")
322 def operation_stopped(self
):
323 get_instance().poll_done(self
._category
)
325 pollmanager_instance
= None
328 global pollmanager_instance
329 if pollmanager_instance
is None:
330 pollmanager_instance
= PollManager()
331 return pollmanager_instance