""" 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()
syntax highlighted by Code2HTML, v. 0.9.1