source: orange-bioinformatics/_bioinformatics/obiKEGG/caching.py @ 1734:91d14dd2cf0e

Revision 1734:91d14dd2cf0e, 7.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 14 months ago (diff)

obiKEGG code style fixes.

RevLine 
[1532]1"""
2Caching framework for cached kegg api calls.
[1734]3
[1532]4"""
5import os
6import UserDict
7import sqlite3
8import cPickle as pickle
9
[1734]10from contextlib import closing
11
[1603]12from datetime import datetime, date, timedelta
13from . import conf
[1532]14
[1734]15
[1532]16class Store(object):
17    def __init__(self):
18        self.timestamp = 0
[1734]19
[1532]20    def open(self):
21        raise NotImplementedError
[1734]22
[1532]23    def __enter__(self):
24        return self
[1734]25
[1532]26    def __exit__(self, *args):
27        pass
28
[1734]29
[1532]30class Sqlite3Store(Store, UserDict.DictMixin):
31    def __init__(self, filename):
32        self.filename = filename
33        self.con = sqlite3.connect(filename)
34        self.con.execute("""
[1734]35            CREATE TABLE IF NOT EXISTS cache
36                (key TEXT UNIQUE,
37                 value TEXT
38                )
[1532]39        """)
[1605]40        self.con.execute("""
[1734]41            CREATE INDEX IF NOT EXISTS cache_index
42            ON cache (key)
[1605]43        """)
[1532]44        self.con.commit()
[1734]45
[1532]46    def __getitem__(self, key):
47        cur = self.con.execute("""
48            SELECT value
49            FROM cache
50            WHERE key=?
51        """, (key,))
52        r = cur.fetchall()
[1734]53
[1532]54        if not r:
55            raise KeyError(key)
56        else:
57            return pickle.loads(str(r[0][0]))
[1734]58
[1532]59    def __setitem__(self, key, value):
60        value = pickle.dumps(value)
61        self.con.execute("""
[1537]62            INSERT OR REPLACE INTO cache
[1532]63            VALUES (?, ?)
64        """, (key, value))
65        self.con.commit()
[1734]66
[1532]67    def __delitem__(self, key):
68        self.con.execute("""
69            DELETE FROM cache
70            WHERE key=?
71        """, (key,))
72        self.con.commit()
[1734]73
[1532]74    def keys(self):
75        cur = self.con.execute("""
76            SELECT key
77            FROM cache
78        """)
79        return [str(r[0]) for r in cur.fetchall()]
[1734]80
[1532]81    def close(self):
82        pass
[1734]83
84
[1532]85class DictStore(Store, UserDict.DictMixin):
86    def __init__(self):
87        Store.__init__(self)
[1734]88
[1532]89    def close(self):
90        pass
91
92
93class cache_entry(object):
94    def __init__(self, value, mtime=None, expires=None):
95        self.value = value
96        self.mtime = mtime
97        self.expires = expires
[1734]98
[1603]99_SESSION_START = datetime.now()
[1532]100
[1734]101
[1532]102class cached_wrapper(object):
103    """
[1734]104    TODO: needs documentation
105    """
106    def __init__(self, function, instance, class_, cache_store,
107                 last_modified=None):
[1532]108        self.function = function
109        self.instance = instance
110        self.class_ = class_
111        self.cache_store = cache_store
112        self.last_modified = last_modified
[1734]113
[1532]114    def has_key(self, key):
115        with closing(self.cache_store()) as store:
116            return key in store
[1734]117
[1532]118    def key_from_args(self, args, kwargs=None):
119        key = self.function.__name__ + repr(args)
120        return key
[1734]121
[1532]122    def invalidate_key(self, key):
123        with closing(self.cache_store()) as store:
124            del store[key]
[1734]125
[1532]126    def last_modified_from_args(self, args, kwargs=None):
127        key = self.key_from_args(args, kwargs)
128        if self.instance is not None:
129            self.instance.last_modified(args)
[1734]130
[1532]131    def invalidate_args(self, args):
132        return self.invalidate_key(self.key_from_args(args))
[1734]133
[1532]134    def invalidate_all(self):
135        prefix = self.key_from_args(()).rstrip(",)")
136        with self.cache_store() as store:
137            for key in store.keys():
138                if key.startswith(prefix):
139                    del store[key]
[1734]140
[1532]141    def memoize(self, args, kwargs, value, timestamp=None):
142        key = self.key_from_args(args, kwargs)
143        if timestamp is None:
144            timestamp = datetime.now()
[1734]145
[1532]146        with closing(self.cache_store()) as store:
147            store[key] = cache_entry(value, mtime=timestamp)
[1734]148
[1532]149    def __call__(self, *args):
150        key = self.key_from_args(args)
151        with closing(self.cache_store()) as store:
152            valid = True
153            if key not in store:
154                valid = False
155            else:
156                entry = store[key]
157                rval = entry.value
[1734]158
[1532]159                if not self.is_entry_valid(entry, args):
160                    valid = False
161            if not valid:
162                rval = self.function(self.instance, *args)
163                store[key] = cache_entry(rval, datetime.now(), None)
[1734]164
[1532]165        return rval
[1734]166
[1532]167    def min_timestamp(self, args):
168        key = self.key_from_args(args)
169        return datetime.fromtimestamp(0)
[1734]170
[1532]171    def is_entry_valid(self, entry, args):
[1603]172        # Need to check datetime first (it subclasses date)
173        if isinstance(entry.mtime, datetime):
174            mtime = entry.mtime
175        elif isinstance(entry.mtime, date):
176            mtime = datetime(entry.mtime.year, entry.mtime.month,
177                             entry.mtime.day, 1, 1, 1)
178        else:
179            return False
[1734]180
[1603]181        if self.min_timestamp(args) > mtime:
182            return False
[1734]183
[1603]184        last_modified = self.last_modified_from_args(args)
[1734]185
[1603]186        if isinstance(last_modified, date):
187            last_modified = datetime(last_modified.year, last_modified.month,
188                                     last_modified.day, 1, 1, 1)
189        elif isinstance(last_modified, basestring):
[1605]190            # Could have different format
[1734]191            mtime = mtime.strftime("%Y %m %d %H %M %S")
192
[1603]193        elif last_modified is None:
194            if conf.params["cache.invalidate"] == "always":
195                return False
196            elif conf.params["cache.invalidate"] == "session":
197                last_modified = _SESSION_START
198            elif conf.params["cache.invalidate"] == "daily":
199                last_modified = datetime.now().replace(hour=0, minute=0,
200                                                       second=0, microsecond=0)
201            elif conf.params["cache.invalidate"] == "weekly":
202                last_modified = datetime.now() - timedelta(7)
[1734]203            else:  # ???
[1603]204                pass
[1605]205        return last_modified <= mtime
[1734]206
207
[1532]208class cached_method(object):
209    def __init__(self, function):
210        self.function = function
[1734]211
[1532]212    def __get__(self, instance, owner):
213        if instance is not None:
214            return cached_wrapper(self.function, instance, owner,
215                                  self.get_cache_store(instance, owner))
216        return self
[1734]217
[1532]218    def get_cache_store(self, instance, owner):
219        if hasattr(instance, "cache_store"):
220            return instance.cache_store
221        elif not hasattr("_cached_method_cache"):
222            instance._cached_method_cache = DictStore()
223        return instance._cached_method_cache
224
225
226class bget_cached_method(cached_method):
227    def __get__(self, instance, owner):
228        if instance is not None:
229            return cached_wrapper(self.function, instance, owner,
230                                  self.get_cache_store(instance, owner),
231                                  self.get_last_modified(instance, owner))
232        return self
[1734]233
[1532]234    def get_last_modified(self, instance, owner):
235        if hasattr(instance, "last_modified"):
236            return instance.last_modified
[1734]237
238
[1532]239def touch_dir(path):
240    path = os.path.expanduser(path)
241    if not os.path.exists(path):
242        os.makedirs(path)
[1734]243
244
[1605]245def clear_cache():
246    """Clear all locally cached KEGG data.
247    """
248    import glob
249    path = conf.params["cache.path"]
250    if os.path.realpath(path) != os.path.realpath(conf.kegg_dir):
[1734]251        raise Exception("Non default cache path. Please remove the contents "
252                        "of %r manually." % path)
253
[1605]254    for cache_filename in glob.glob(os.path.join(path, "*.sqlite3")):
255        os.remove(cache_filename)
[1734]256
[1605]257    for ko_filename in glob.glob(os.path.join(path, "*.keg")):
258        os.remove(ko_filename)
[1734]259
[1605]260    for kgml_filename in glob.glob(os.path.join(path, "*.xml")):
261        os.remove(kgml_filename)
[1734]262
[1605]263    for png_filename in glob.glob(os.path.join(path, "*.png")):
264        os.remove(png_filename)
Note: See TracBrowser for help on using the repository browser.