Don't remove .c file for dumper binary
[gnome.gobject-introspection] / giscanner / cachestore.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import errno
22 import cPickle
23 import hashlib
24 import os
25 import shutil
26 import tempfile
27
28
29 def _get_cachedir():
30     homedir = os.environ.get('HOME')
31     if homedir is None:
32         return None
33     if not os.path.exists(homedir):
34         return None
35
36     cachedir = os.path.join(homedir, '.cache')
37     if not os.path.exists(cachedir):
38         try:
39             os.mkdir(cachedir, 0755)
40         except OSError:
41             return None
42
43     scannerdir = os.path.join(cachedir, 'g-ir-scanner')
44     if not os.path.exists(scannerdir):
45         os.mkdir(scannerdir, 0755)
46     # If it exists and is a file, don't cache at all
47     elif not os.path.isdir(scannerdir):
48         return None
49     return scannerdir
50
51
52 class CacheStore(object):
53
54     def __init__(self):
55         try:
56             self._directory = _get_cachedir()
57         except OSError, e:
58             if e.errno != errno.EPERM:
59                 raise
60             self._directory = None
61
62     def _get_filename(self, filename):
63         # If we couldn't create the directory we're probably
64         # on a read only home directory where we just disable
65         # the cache all together.
66         if self._directory is None:
67             return
68         hexdigest = hashlib.sha1(filename).hexdigest()
69         return os.path.join(self._directory, hexdigest)
70
71     def _cache_is_valid(self, store_filename, filename):
72         return (os.stat(store_filename).st_mtime >=
73                 os.stat(filename).st_mtime)
74
75     def _remove_filename(self, filename):
76         try:
77             os.unlink(filename)
78         except IOError, e:
79             # Permission denied
80             if e.errno == errno.EACCES:
81                 return
82             else:
83                 raise
84         except OSError, e:
85             # File does not exist
86             if e.errno == errno.ENOENT:
87                 return
88             else:
89                 raise
90
91     def store(self, filename, data):
92         store_filename = self._get_filename(filename)
93         if store_filename is None:
94             return
95
96         if (os.path.exists(store_filename) and
97             self._cache_is_valid(store_filename, filename)):
98             return None
99
100         tmp_fd, tmp_filename = tempfile.mkstemp(prefix='g-ir-scanner-cache-')
101         try:
102             cPickle.dump(data, os.fdopen(tmp_fd, 'w'))
103         except IOError, e:
104             # No space left on device
105             if e.errno == errno.ENOSPC:
106                 self._remove_filename(tmp_filename)
107                 return
108             else:
109                 raise
110
111         try:
112             shutil.move(tmp_filename, store_filename)
113         except IOError, e:
114             # Permission denied
115             if e.errno == errno.EACCES:
116                 self._remove_filename(tmp_filename)
117             else:
118                 raise
119
120     def load(self, filename):
121         store_filename = self._get_filename(filename)
122         if store_filename is None:
123             return
124         try:
125             fd = open(store_filename)
126         except IOError, e:
127             if e.errno == errno.ENOENT:
128                 return None
129             else:
130                 raise
131         if not self._cache_is_valid(store_filename, filename):
132             return None
133         try:
134             data = cPickle.load(fd)
135         except (AttributeError, EOFError, ValueError, cPickle.BadPickleGet):
136             # Broken cache entry, remove it
137             self._remove_filename(store_filename)
138             data = None
139         return data