You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
337 lines
8.9 KiB
337 lines
8.9 KiB
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
|
# published by the Free Software Foundation; either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program 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 Library Lesser General Public License for more details at
|
|
# ( http://www.gnu.org/licenses/lgpl.html ).
|
|
#
|
|
# You should have received a copy of the GNU Lesser 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.
|
|
# written by: Jeff Ortel ( jortel@redhat.com )
|
|
|
|
"""
|
|
Contains basic caching classes.
|
|
"""
|
|
|
|
import os
|
|
import suds
|
|
from tempfile import gettempdir as tmp
|
|
from suds.transport import *
|
|
from suds.sax.parser import Parser
|
|
from suds.sax.element import Element
|
|
from datetime import datetime as dt
|
|
from datetime import timedelta
|
|
from cStringIO import StringIO
|
|
from logging import getLogger
|
|
try:
|
|
import cPickle as pickle
|
|
except:
|
|
import pickle
|
|
|
|
log = getLogger(__name__)
|
|
|
|
|
|
class Cache:
|
|
"""
|
|
An object object cache.
|
|
"""
|
|
|
|
def get(self, id):
|
|
"""
|
|
Get a object from the cache by ID.
|
|
@param id: The object ID.
|
|
@type id: str
|
|
@return: The object, else None
|
|
@rtype: any
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
def getf(self, id):
|
|
"""
|
|
Get a object from the cache by ID.
|
|
@param id: The object ID.
|
|
@type id: str
|
|
@return: The object, else None
|
|
@rtype: any
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
def put(self, id, object):
|
|
"""
|
|
Put a object into the cache.
|
|
@param id: The object ID.
|
|
@type id: str
|
|
@param object: The object to add.
|
|
@type object: any
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
def putf(self, id, fp):
|
|
"""
|
|
Write a fp into the cache.
|
|
@param id: The object ID.
|
|
@type id: str
|
|
@param fp: File pointer.
|
|
@type fp: file-like object.
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
def purge(self, id):
|
|
"""
|
|
Purge a object from the cache by id.
|
|
@param id: A object ID.
|
|
@type id: str
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
def clear(self):
|
|
"""
|
|
Clear all objects from the cache.
|
|
"""
|
|
raise Exception('not-implemented')
|
|
|
|
|
|
class NoCache(Cache):
|
|
"""
|
|
The passthru object cache.
|
|
"""
|
|
|
|
def get(self, id):
|
|
return None
|
|
|
|
def getf(self, id):
|
|
return None
|
|
|
|
def put(self, id, object):
|
|
pass
|
|
|
|
def putf(self, id, fp):
|
|
pass
|
|
|
|
|
|
class FileCache(Cache):
|
|
"""
|
|
A file-based URL cache.
|
|
@cvar fnprefix: The file name prefix.
|
|
@type fnsuffix: str
|
|
@ivar duration: The cached file duration which defines how
|
|
long the file will be cached.
|
|
@type duration: (unit, value)
|
|
@ivar location: The directory for the cached files.
|
|
@type location: str
|
|
"""
|
|
fnprefix = 'suds'
|
|
units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds')
|
|
|
|
def __init__(self, location=None, **duration):
|
|
"""
|
|
@param location: The directory for the cached files.
|
|
@type location: str
|
|
@param duration: The cached file duration which defines how
|
|
long the file will be cached. A duration=0 means forever.
|
|
The duration may be: (months|weeks|days|hours|minutes|seconds).
|
|
@type duration: {unit:value}
|
|
"""
|
|
if location is None:
|
|
location = os.path.join(tmp(), 'suds')
|
|
self.location = location
|
|
self.duration = (None, 0)
|
|
self.setduration(**duration)
|
|
self.checkversion()
|
|
|
|
def fnsuffix(self):
|
|
"""
|
|
Get the file name suffix
|
|
@return: The suffix
|
|
@rtype: str
|
|
"""
|
|
return 'gcf'
|
|
|
|
def setduration(self, **duration):
|
|
"""
|
|
Set the caching duration which defines how long the
|
|
file will be cached.
|
|
@param duration: The cached file duration which defines how
|
|
long the file will be cached. A duration=0 means forever.
|
|
The duration may be: (months|weeks|days|hours|minutes|seconds).
|
|
@type duration: {unit:value}
|
|
"""
|
|
if len(duration) == 1:
|
|
arg = duration.items()[0]
|
|
if not arg[0] in self.units:
|
|
raise Exception('must be: %s' % str(self.units))
|
|
self.duration = arg
|
|
return self
|
|
|
|
def setlocation(self, location):
|
|
"""
|
|
Set the location (directory) for the cached files.
|
|
@param location: The directory for the cached files.
|
|
@type location: str
|
|
"""
|
|
self.location = location
|
|
|
|
def mktmp(self):
|
|
"""
|
|
Make the I{location} directory if it doesn't already exits.
|
|
"""
|
|
try:
|
|
if not os.path.isdir(self.location):
|
|
os.makedirs(self.location)
|
|
except:
|
|
log.debug(self.location, exc_info=1)
|
|
return self
|
|
|
|
def put(self, id, bfr):
|
|
try:
|
|
fn = self.__fn(id)
|
|
f = self.open(fn, 'w')
|
|
f.write(bfr)
|
|
f.close()
|
|
return bfr
|
|
except:
|
|
log.debug(id, exc_info=1)
|
|
return bfr
|
|
|
|
def putf(self, id, fp):
|
|
try:
|
|
fn = self.__fn(id)
|
|
f = self.open(fn, 'w')
|
|
f.write(fp.read())
|
|
fp.close()
|
|
f.close()
|
|
return open(fn)
|
|
except:
|
|
log.debug(id, exc_info=1)
|
|
return fp
|
|
|
|
def get(self, id):
|
|
try:
|
|
f = self.getf(id)
|
|
bfr = f.read()
|
|
f.close()
|
|
return bfr
|
|
except:
|
|
pass
|
|
|
|
def getf(self, id):
|
|
try:
|
|
fn = self.__fn(id)
|
|
self.validate(fn)
|
|
return self.open(fn)
|
|
except:
|
|
pass
|
|
|
|
def validate(self, fn):
|
|
"""
|
|
Validate that the file has not expired based on the I{duration}.
|
|
@param fn: The file name.
|
|
@type fn: str
|
|
"""
|
|
if self.duration[1] < 1:
|
|
return
|
|
created = dt.fromtimestamp(os.path.getctime(fn))
|
|
d = { self.duration[0]:self.duration[1] }
|
|
expired = created+timedelta(**d)
|
|
if expired < dt.now():
|
|
log.debug('%s expired, deleted', fn)
|
|
os.remove(fn)
|
|
|
|
def clear(self):
|
|
for fn in os.listdir(self.location):
|
|
if os.path.isdir(fn):
|
|
continue
|
|
if fn.startswith(self.fnprefix):
|
|
log.debug('deleted: %s', fn)
|
|
os.remove(os.path.join(self.location, fn))
|
|
|
|
def purge(self, id):
|
|
fn = self.__fn(id)
|
|
try:
|
|
os.remove(fn)
|
|
except:
|
|
pass
|
|
|
|
def open(self, fn, *args):
|
|
"""
|
|
Open the cache file making sure the directory is created.
|
|
"""
|
|
self.mktmp()
|
|
return open(fn, *args)
|
|
|
|
def checkversion(self):
|
|
path = os.path.join(self.location, 'version')
|
|
try:
|
|
|
|
f = self.open(path)
|
|
version = f.read()
|
|
f.close()
|
|
if version != suds.__version__:
|
|
raise Exception()
|
|
except:
|
|
self.clear()
|
|
f = self.open(path, 'w')
|
|
f.write(suds.__version__)
|
|
f.close()
|
|
|
|
def __fn(self, id):
|
|
name = id
|
|
suffix = self.fnsuffix()
|
|
fn = '%s-%s.%s' % (self.fnprefix, name, suffix)
|
|
return os.path.join(self.location, fn)
|
|
|
|
|
|
class DocumentCache(FileCache):
|
|
"""
|
|
Provides xml document caching.
|
|
"""
|
|
|
|
def fnsuffix(self):
|
|
return 'xml'
|
|
|
|
def get(self, id):
|
|
try:
|
|
fp = FileCache.getf(self, id)
|
|
if fp is None:
|
|
return None
|
|
p = Parser()
|
|
return p.parse(fp)
|
|
except:
|
|
FileCache.purge(self, id)
|
|
|
|
def put(self, id, object):
|
|
if isinstance(object, Element):
|
|
FileCache.put(self, id, str(object))
|
|
return object
|
|
|
|
|
|
class ObjectCache(FileCache):
|
|
"""
|
|
Provides pickled object caching.
|
|
@cvar protocol: The pickling protocol.
|
|
@type protocol: int
|
|
"""
|
|
protocol = 2
|
|
|
|
def fnsuffix(self):
|
|
return 'px'
|
|
|
|
def get(self, id):
|
|
try:
|
|
fp = FileCache.getf(self, id)
|
|
if fp is None:
|
|
return None
|
|
else:
|
|
return pickle.load(fp)
|
|
except:
|
|
FileCache.purge(self, id)
|
|
|
|
def put(self, id, object):
|
|
bfr = pickle.dumps(object, self.protocol)
|
|
FileCache.put(self, id, bfr)
|
|
return object
|
|
|