""" ImageCache.py Module for handling images """ __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc." __license__ = """ Straw is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Straw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ import NetworkConstants import Event import URLFetch import PollManager import ItemStore import utils import error import Config STATUS_IMAGE_WAITING = None STATUS_IMAGE_BROKEN = None class CacheEntry(object): def __init__(self, image, count, pollstopper = None, restore = False): self._image = image self._count = count self._pollstopper = pollstopper if not restore: self._save_count() @apply def image(): doc="The image object that is associated with this class" def fget(self): return self._image def fset(self, image): self._image = image return property(**locals()) @apply def count(): doc="The number of instances of this image" def fget(self): return self._count def fset(self, count): self._count = count self._save_count() return property(**locals()) @apply def pollstopper(): doc="the stopper object for this cache entry" def fget(self): return self._pollstopper def fset(self, pollstopper): self._pollstopper = pollstopper if pollstopper: pollstopper.signal_connect(Event.PollingStoppedSignal, self._polling_stopped) return property(**locals()) def incr_count(self): self._count += 1 def decr_count(self): self._count -= 1 def _polling_stopped(self): self._pollstopper = None def _save_count(self): ItemStore.get_instance().set_image_count(self._image.url, self._count) class Cache(Event.SignalEmitter): """Image cache with explicit reference counting.""" def __init__(self): Event.SignalEmitter.__init__(self) self.initialize_slots(Event.ImageUpdatedSignal) # self.__cache contains items of the form [Image, refcount] self.__cache = dict() def __getitem__(self, key): return self.__cache[key].image def add_refer(self, key, restore = False, item = None): if key not in self.__cache: if not restore: # fetch image image = Image(key, Image.WAITING) ic = ImageConsumer(image) headers = {} stopper = None if item and item.feed: headers['Referer'] = item.feed.location try: stopper = URLFetch.connection_manager.request( key, ic, priority=NetworkConstants.PRIORITY_IMAGE, headers=headers) except URLFetch.RequestSchemeException, e: ic.http_failed(e) except Exception, e: error.logtb("ImageCache.add_refer: ", str(e)) ic.http_failed(e) self.__cache[key] = CacheEntry( image, 1, PollManager.PollStopper(stopper, image), restore=restore) else: image = Image(key, Image.DATA_IN_DB) self.__cache[key] = CacheEntry(image, 1, pollstopper=None, restore=restore) elif key in self.__cache and not restore: self.__cache[key].incr_count() def remove_refer(self, key): if self.__cache.has_key(key): entry = self.__cache[key] entry.decr_count() if entry.count == 0: del self.__cache[key] self.emit_signal(Event.ImageUpdatedSignal(self, key, None)) def image_updated(self, url, data): self.__cache[url].pollstopper = None self.emit_signal(Event.ImageUpdatedSignal(self, url, data)) def set_image(self, url, image): if self.__cache.has_key(url): self.__cache[url].incr_count() else: self.__cache[url] = CacheEntry(image, 1) def set_image_with_count(self, url, image, count, restore=False): self.__cache[url] = CacheEntry(image, count, restore=restore) def stop_transfer(self, url): image = self.__cache.get(url, None) if image and image.pollstopper: self.__cache[url].pollstopper.stop() self.__cache[url].pollstopper = None class Image: WAITING = 1 DATA_IN_DB = 2 FAILED = 3 def __init__(self, url, status = DATA_IN_DB): self.url = url self.status = status def get_data(self): if self.status == self.WAITING: return STATUS_IMAGE_WAITING elif self.status == self.DATA_IN_DB: data = self.read_data() if data is None: self.status = self.FAILED return STATUS_IMAGE_BROKEN return data else: return STATUS_IMAGE_BROKEN def set_data(self, data): cache.image_updated(self.url, data) self.status = self.DATA_IN_DB def set_failed(self): self.status = self.FAILED def read_data(self): istore = ItemStore.get_instance() return istore.read_image(self.url) def _return_id(self): return "%d %s" % (id(self), self.url) cache = Cache() class ImageConsumer: def __init__(self, imobj): self._imobj = imobj def http_results(self, status, headers, data): if status[1] == 200: self._imobj.set_data(data) else: self._imobj.set_failed() def http_failed(self, exception): self._imobj.set_failed() def operation_stopped(self): self._imobj.set_failed() def initialize(): global STATUS_IMAGE_WAITING global STATUS_IMAGE_BROKEN libdir = utils.find_image_dir() STATUS_IMAGE_WAITING = open( libdir + "/image-waiting.png").read() STATUS_IMAGE_BROKEN = open( libdir + "/image-broken.png").read()