root/galaxy-central/eggs/Beaker-1.4-py2.6.egg/beaker/container.py @ 3

リビジョン 3, 18.4 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""Container and Namespace classes"""
2import anydbm
3import cPickle
4import logging
5import os.path
6import time
7
8import beaker.util as util
9from beaker.exceptions import CreationAbortedError, MissingCacheParameter
10from beaker.synchronization import _threading, file_synchronizer, \
11     mutex_synchronizer, NameLock, null_synchronizer
12
13__all__ = ['Value', 'Container', 'ContainerContext',
14           'MemoryContainer', 'DBMContainer', 'NamespaceManager',
15           'MemoryNamespaceManager', 'DBMNamespaceManager', 'FileContainer',
16           'OpenResourceNamespaceManager',
17           'FileNamespaceManager', 'CreationAbortedError']
18
19
20logger = logging.getLogger('beaker.container')
21if logger.isEnabledFor(logging.DEBUG):
22    debug = logger.debug
23else:
24    def debug(message, *args):
25        pass
26
27
28class NamespaceManager(object):
29    """Handles dictionary operations and locking for a namespace of
30    values.
31   
32    The implementation for setting and retrieving the namespace data is
33    handled by subclasses.
34   
35    NamespaceManager may be used alone, or may be privately accessed by
36    one or more Container objects.  Container objects provide per-key
37    services like expiration times and automatic recreation of values.
38   
39    Multiple NamespaceManagers created with a particular name will all
40    share access to the same underlying datasource and will attempt to
41    synchronize against a common mutex object.  The scope of this
42    sharing may be within a single process or across multiple
43    processes, depending on the type of NamespaceManager used.
44   
45    The NamespaceManager itself is generally threadsafe, except in the
46    case of the DBMNamespaceManager in conjunction with the gdbm dbm
47    implementation.
48
49    """
50    def __init__(self, namespace):
51        self.namespace = namespace
52       
53    def get_creation_lock(self, key):
54        raise NotImplementedError()
55
56    def do_remove(self):
57        raise NotImplementedError()
58
59    def acquire_read_lock(self):
60        pass
61
62    def release_read_lock(self):
63        pass
64
65    def acquire_write_lock(self, wait=True):
66        return True
67
68    def release_write_lock(self):
69        pass
70
71    def has_key(self, key):
72        return self.__contains__(key)
73
74    def __getitem__(self, key):
75        raise NotImplementedError()
76       
77    def __setitem__(self, key, value):
78        raise NotImplementedError()
79   
80    def set_value(self, key, value, expiretime=None):
81        """Optional set_value() method called by Value.
82       
83        Allows an expiretime to be passed, for namespace
84        implementations which can prune their collections
85        using expiretime.
86       
87        """
88        self[key] = value
89       
90    def __contains__(self, key):
91        raise NotImplementedError()
92
93    def __delitem__(self, key):
94        raise NotImplementedError()
95   
96    def keys(self):
97        raise NotImplementedError()
98   
99    def remove(self):
100        self.do_remove()
101       
102
103class OpenResourceNamespaceManager(NamespaceManager):
104    """A NamespaceManager where read/write operations require opening/
105    closing of a resource which is possibly mutexed.
106   
107    """
108    def __init__(self, namespace):
109        NamespaceManager.__init__(self, namespace)
110        self.access_lock = self.get_access_lock()
111        self.openers = 0
112        self.mutex = _threading.Lock()
113
114    def get_access_lock(self):
115        raise NotImplementedError()
116
117    def do_open(self, flags):
118        raise NotImplementedError()
119
120    def do_close(self):
121        raise NotImplementedError()
122
123    def acquire_read_lock(self):
124        self.access_lock.acquire_read_lock()
125        try:
126            self.open('r', checkcount = True)
127        except:
128            self.access_lock.release_read_lock()
129            raise
130           
131    def release_read_lock(self):
132        try:
133            self.close(checkcount = True)
134        finally:
135            self.access_lock.release_read_lock()
136       
137    def acquire_write_lock(self, wait=True):
138        r = self.access_lock.acquire_write_lock(wait)
139        try:
140            if (wait or r):
141                self.open('c', checkcount = True)
142            return r
143        except:
144            self.access_lock.release_write_lock()
145            raise
146           
147    def release_write_lock(self):
148        try:
149            self.close(checkcount=True)
150        finally:
151            self.access_lock.release_write_lock()
152
153    def open(self, flags, checkcount=False):
154        self.mutex.acquire()
155        try:
156            if checkcount:
157                if self.openers == 0:
158                    self.do_open(flags)
159                self.openers += 1
160            else:
161                self.do_open(flags)
162                self.openers = 1
163        finally:
164            self.mutex.release()
165
166    def close(self, checkcount=False):
167        self.mutex.acquire()
168        try:
169            if checkcount:
170                self.openers -= 1
171                if self.openers == 0:
172                    self.do_close()
173            else:
174                if self.openers > 0:
175                    self.do_close()
176                self.openers = 0
177        finally:
178            self.mutex.release()
179
180    def remove(self):
181        self.access_lock.acquire_write_lock()
182        try:
183            self.close(checkcount=False)
184            self.do_remove()
185        finally:
186            self.access_lock.release_write_lock()
187
188class Value(object):
189    __slots__ = 'key', 'createfunc', 'expiretime', 'expire_argument', 'starttime', 'storedtime',\
190                'namespace'
191
192    def __init__(self, key, namespace, createfunc=None, expiretime=None, starttime=None):
193        self.key = key
194        self.createfunc = createfunc
195        self.expire_argument = expiretime
196        self.starttime = starttime
197        self.storedtime = -1
198        self.namespace = namespace
199
200    def has_value(self):
201        """return true if the container has a value stored.
202
203        This is regardless of it being expired or not.
204
205        """
206        self.namespace.acquire_read_lock()
207        try:   
208            return self.namespace.has_key(self.key)
209        finally:
210            self.namespace.release_read_lock()
211
212    def can_have_value(self):
213        return self.has_current_value() or self.createfunc is not None 
214
215    def has_current_value(self):
216        self.namespace.acquire_read_lock()
217        try:   
218            has_value = self.namespace.has_key(self.key)
219            if has_value:
220                value = self.__get_value()
221                return not self._is_expired()
222            else:
223                return False
224        finally:
225            self.namespace.release_read_lock()
226
227    def _is_expired(self):
228        """Return true if this container's value is expired.
229       
230        Note that this method is only correct if has_current_value()
231        or get_value() have been called already.
232       
233        """
234        return (
235            (
236                self.starttime is not None and
237                self.storedtime < self.starttime
238            )
239            or
240            (
241                self.expiretime is not None and
242                time.time() >= self.expiretime + self.storedtime
243            )
244        )
245
246    def get_value(self):
247        self.namespace.acquire_read_lock()
248        try:
249            has_value = self.has_value()
250            if has_value:
251                try:
252                    value = self.__get_value()
253                    if not self._is_expired():
254                        return value
255                except KeyError:
256                    # guard against un-mutexed backends raising KeyError
257                    pass
258                   
259            if not self.createfunc:
260                raise KeyError(self.key)
261        finally:
262            self.namespace.release_read_lock()
263
264        has_createlock = False
265        creation_lock = self.namespace.get_creation_lock(self.key)
266        if has_value:
267            if not creation_lock.acquire(wait=False):
268                debug("get_value returning old value while new one is created")
269                return value
270            else:
271                debug("lock_creatfunc (didnt wait)")
272                has_createlock = True
273
274        if not has_createlock:
275            debug("lock_createfunc (waiting)")
276            creation_lock.acquire()
277            debug("lock_createfunc (waited)")
278
279        try:
280            # see if someone created the value already
281            self.namespace.acquire_read_lock()
282            try:
283                if self.has_value():
284                    try:
285                        value = self.__get_value()
286                        if not self._is_expired():
287                            return value
288                    except KeyError:
289                        # guard against un-mutexed backends raising KeyError
290                        pass
291            finally:
292                self.namespace.release_read_lock()
293
294            debug("get_value creating new value")
295            v = self.createfunc()
296            self.set_value(v)
297            return v
298        finally:
299            creation_lock.release()
300            debug("released create lock")
301
302    def __get_value(self):
303        value = self.namespace[self.key]
304        try:
305            self.storedtime, self.expiretime, value = value
306        except ValueError:
307            if not len(value) == 2:
308                raise
309            # Old format: upgrade
310            self.storedtime, value = value
311            self.expiretime = self.expire_argument = None
312            debug("get_value upgrading time %r expire time %r", self.storedtime, self.expire_argument)
313            self.namespace.release_read_lock()
314            self.set_value(value)
315            self.namespace.acquire_read_lock()
316        return value
317
318    def set_value(self, value):
319        self.namespace.acquire_write_lock()
320        try:
321            self.storedtime = time.time()
322            debug("set_value stored time %r expire time %r", self.storedtime, self.expire_argument)
323            self.namespace.set_value(self.key, (self.storedtime, self.expire_argument, value))
324        finally:
325            self.namespace.release_write_lock()
326
327    def clear_value(self):
328        self.namespace.acquire_write_lock()
329        try:
330            debug("clear_value")
331            if self.namespace.has_key(self.key):
332                try:
333                    del self.namespace[self.key]
334                except KeyError:
335                    # guard against un-mutexed backends raising KeyError
336                    pass
337            self.storedtime = -1
338        finally:
339            self.namespace.release_write_lock()
340
341
342class MemoryNamespaceManager(NamespaceManager):
343    namespaces = util.SyncDict()
344
345    def __init__(self, namespace, **kwargs):
346        NamespaceManager.__init__(self, namespace)
347        self.dictionary = MemoryNamespaceManager.namespaces.get(self.namespace,
348                                                                dict)
349    def get_creation_lock(self, key):
350        return NameLock(
351            identifier="memorycontainer/funclock/%s/%s" % (self.namespace, key),
352            reentrant=True
353        )
354
355    def __getitem__(self, key):
356        return self.dictionary[key]
357
358    def __contains__(self, key):
359        return self.dictionary.__contains__(key)
360
361    def has_key(self, key):
362        return self.dictionary.__contains__(key)
363       
364    def __setitem__(self, key, value):
365        self.dictionary[key] = value
366   
367    def __delitem__(self, key):
368        del self.dictionary[key]
369
370    def do_remove(self):
371        self.dictionary.clear()
372       
373    def keys(self):
374        return self.dictionary.keys()
375
376
377class DBMNamespaceManager(OpenResourceNamespaceManager):
378    def __init__(self, namespace, dbmmodule=None, data_dir=None,
379            dbm_dir=None, lock_dir=None, digest_filenames=True, **kwargs):
380        self.digest_filenames = digest_filenames
381       
382        if not dbm_dir and not data_dir:
383            raise MissingCacheParameter("data_dir or dbm_dir is required")
384        elif dbm_dir:
385            self.dbm_dir = dbm_dir
386        else:
387            self.dbm_dir = data_dir + "/container_dbm"
388        util.verify_directory(self.dbm_dir)
389       
390        if not lock_dir and not data_dir:
391            raise MissingCacheParameter("data_dir or lock_dir is required")
392        elif lock_dir:
393            self.lock_dir = lock_dir
394        else:
395            self.lock_dir = data_dir + "/container_dbm_lock"
396        util.verify_directory(self.lock_dir)
397
398        self.dbmmodule = dbmmodule or anydbm
399
400        self.dbm = None
401        OpenResourceNamespaceManager.__init__(self, namespace)
402
403        self.file = util.encoded_path(root= self.dbm_dir,
404                                      identifiers=[self.namespace],
405                                      extension='.dbm',
406                                      digest_filenames=self.digest_filenames)
407       
408        debug("data file %s", self.file)
409        self._checkfile()
410
411    def get_access_lock(self):
412        return file_synchronizer(identifier=self.namespace,
413                                 lock_dir=self.lock_dir)
414                                 
415    def get_creation_lock(self, key):
416        return file_synchronizer(
417                    identifier = "dbmcontainer/funclock/%s" % self.namespace,
418                    lock_dir=self.lock_dir
419                )
420
421    def file_exists(self, file):
422        if os.access(file, os.F_OK):
423            return True
424        else:
425            for ext in ('db', 'dat', 'pag', 'dir'):
426                if os.access(file + os.extsep + ext, os.F_OK):
427                    return True
428                   
429        return False
430   
431    def _checkfile(self):
432        if not self.file_exists(self.file):
433            g = self.dbmmodule.open(self.file, 'c')
434            g.close()
435               
436    def get_filenames(self):
437        list = []
438        if os.access(self.file, os.F_OK):
439            list.append(self.file)
440           
441        for ext in ('pag', 'dir', 'db', 'dat'):
442            if os.access(self.file + os.extsep + ext, os.F_OK):
443                list.append(self.file + os.extsep + ext)
444        return list
445
446    def do_open(self, flags):
447        debug("opening dbm file %s", self.file)
448        try:
449            self.dbm = self.dbmmodule.open(self.file, flags)
450        except:
451            self._checkfile()
452            self.dbm = self.dbmmodule.open(self.file, flags)
453
454    def do_close(self):
455        if self.dbm is not None:
456            debug("closing dbm file %s", self.file)
457            self.dbm.close()
458       
459    def do_remove(self):
460        for f in self.get_filenames():
461            os.remove(f)
462       
463    def __getitem__(self, key):
464        return cPickle.loads(self.dbm[key])
465
466    def __contains__(self, key):
467        return self.dbm.has_key(key)
468       
469    def __setitem__(self, key, value):
470        self.dbm[key] = cPickle.dumps(value)
471
472    def __delitem__(self, key):
473        del self.dbm[key]
474
475    def keys(self):
476        return self.dbm.keys()
477
478
479class FileNamespaceManager(OpenResourceNamespaceManager):
480    def __init__(self, namespace, data_dir=None, file_dir=None, lock_dir=None,
481                 digest_filenames=True, **kwargs):
482        self.digest_filenames = digest_filenames
483       
484        if not file_dir and not data_dir:
485            raise MissingCacheParameter("data_dir or file_dir is required")
486        elif file_dir:
487            self.file_dir = file_dir
488        else:
489            self.file_dir = data_dir + "/container_file"
490        util.verify_directory(self.file_dir)
491
492        if not lock_dir and not data_dir:
493            raise MissingCacheParameter("data_dir or lock_dir is required")
494        elif lock_dir:
495            self.lock_dir = lock_dir
496        else:
497            self.lock_dir = data_dir + "/container_file_lock"
498        util.verify_directory(self.lock_dir)
499        OpenResourceNamespaceManager.__init__(self, namespace)
500
501        self.file = util.encoded_path(root=self.file_dir,
502                                      identifiers=[self.namespace],
503                                      extension='.cache',
504                                      digest_filenames=self.digest_filenames)
505        self.hash = {}
506       
507        debug("data file %s", self.file)
508
509    def get_access_lock(self):
510        return file_synchronizer(identifier=self.namespace,
511                                 lock_dir=self.lock_dir)
512                                 
513    def get_creation_lock(self, key):
514        return file_synchronizer(
515                identifier = "filecontainer/funclock/%s" % self.namespace,
516                lock_dir = self.lock_dir
517                )
518       
519    def file_exists(self, file):
520        return os.access(file, os.F_OK)
521
522    def do_open(self, flags):
523        if self.file_exists(self.file):
524            fh = open(self.file, 'rb')
525            try:
526                self.hash = cPickle.load(fh)
527            except (IOError, OSError, EOFError, cPickle.PickleError, ValueError):
528                pass
529            fh.close()
530
531        self.flags = flags
532       
533    def do_close(self):
534        if self.flags == 'c' or self.flags == 'w':
535            fh = open(self.file, 'wb')
536            cPickle.dump(self.hash, fh)
537            fh.close()
538
539        self.hash = {}
540        self.flags = None
541               
542    def do_remove(self):
543        os.remove(self.file)
544        self.hash = {}
545       
546    def __getitem__(self, key):
547        return self.hash[key]
548
549    def __contains__(self, key):
550        return self.hash.has_key(key)
551       
552    def __setitem__(self, key, value):
553        self.hash[key] = value
554
555    def __delitem__(self, key):
556        del self.hash[key]
557
558    def keys(self):
559        return self.hash.keys()
560
561
562#### legacy stuff to support the old "Container" class interface
563
564namespace_classes = {}
565
566ContainerContext = dict
567   
568class ContainerMeta(type):
569    def __init__(cls, classname, bases, dict_):
570        namespace_classes[cls] = cls.namespace_class
571        return type.__init__(cls, classname, bases, dict_)
572    def __call__(self, key, context, namespace, createfunc=None,
573                 expiretime=None, starttime=None, **kwargs):
574        if namespace in context:
575            ns = context[namespace]
576        else:
577            nscls = namespace_classes[self]
578            context[namespace] = ns = nscls(namespace, **kwargs)
579        return Value(key, ns, createfunc=createfunc,
580                     expiretime=expiretime, starttime=starttime)
581
582class Container(object):
583    __metaclass__ = ContainerMeta
584    namespace_class = NamespaceManager
585
586class FileContainer(Container):
587    namespace_class = FileNamespaceManager
588
589class MemoryContainer(Container):
590    namespace_class = MemoryNamespaceManager
591
592class DBMContainer(Container):
593    namespace_class = DBMNamespaceManager
594
595DbmContainer = DBMContainer
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。