1
2
3
4
5
6 """
7 OMERO Support for temporary files and directories
8 """
9
10 import os
11 import sys
12 import atexit
13 import getpass
14 import logging
15 import tempfile
16 import threading
17 import traceback
18 import exceptions
19 import portalocker
20
21 from path import path
22
23
24
25
26
27
28
30 """
31 Creates temporary files and folders and makes a best effort
32 to remove them on exit (or sooner). Typically only a single
33 instance of this class will exist ("manager" variable in this
34 module below)
35 """
36
38 """
39 Initializes a TempFileManager instance with a userDir containing
40 the given prefix value, or "omero" by default. Also registers
41 an atexit callback to call self.cleanup() on exit.
42 """
43 self.logger = logging.getLogger("omero.util.TempFileManager")
44 self.is_win32 = ( sys.platform == "win32" )
45 self.prefix = prefix
46
47 self.userdir = self.tmpdir() / ("%s_%s" % (self.prefix, self.username()))
48 """
49 User-accessible directory of the form $TMPDIR/omero_$USERNAME.
50 If the given directory is not writable, an attempt is made
51 to use an alternative
52 """
53 if not self.create(self.userdir) and not self.access(self.userdir):
54 i = 0
55 while i < 10:
56 t = path("%s_%s" % (self.userdir, i))
57 if self.create(t) or self.access(t):
58 self.userdir = t
59 break
60 raise exceptions.Exception("Failed to create temporary directory: %s" % self.userdir)
61 self.dir = self.userdir / self.pid()
62 """
63 Directory under which all temporary files and folders will be created.
64 An attempt to remove a path not in this directory will lead to an
65 exception.
66 """
67
68
69
70 if not self.dir.exists():
71 self.dir.makedirs()
72 self.logger.debug("Using temp dir: %s" % self.dir)
73
74 self.lock = None
75 try:
76 self.lock = open(str(self.dir / ".lock"), "a+")
77 """
78 .lock file under self.dir which is used to prevent other
79 TempFileManager instances (also in other languages) from
80 cleaning up this directory.
81 """
82 try:
83 portalocker.lock(self.lock, portalocker.LOCK_EX|portalocker.LOCK_NB)
84 atexit.register(self.cleanup)
85 except:
86 lock = self.lock
87 self.lock = None
88 if lock:
89 self.lock.close()
90 raise
91 finally:
92 try:
93 if not self.lock:
94 self.cleanup()
95 except:
96 self.logger.warn("Error on cleanup after error", exc_info = True)
97
99 """
100 Releases self.lock and deletes self.dir.
101 The lock is released first since on some platforms like Windows
102 the lock file cannot be deleted even by the owner of the lock.
103 """
104 try:
105 if self.lock:
106 self.lock.close()
107 except:
108 self.logger.error("Failed to release lock", exc_info = True)
109 self.clean_tempdir()
110
112 """
113 Returns a platform-specific user-writable temporary directory
114 """
115 try:
116 from win32com.shell import shellcon, shell
117 homedir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
118 except ImportError:
119 homedir = os.path.expanduser("~")
120 return path(homedir) / "omero" / "tmp"
121
123 """
124 Returns the current OS-user's name
125 """
126 try:
127 return getpass.getuser()
128 except ImportError:
129 import win32api
130 return win32api.GetUserName()
131
133 """
134 Returns some representation of the current process's id
135 """
136 return str(os.getpid())
137
139 """
140 Returns True if the current user can write to the given directory
141 """
142 dir = str(dir)
143 return os.access(dir, os.W_OK)
144
146 """
147 If the given directory doesn't exist, creates it (with mode 0700) and returns True.
148 Otherwise False.
149 """
150 dir = path(dir)
151 if not dir.exists():
152 dir.makedirs(0700)
153 return True
154 return False
155
157 """
158 Returns the directory under which all temporary
159 files and folders will be created.
160 """
161 return self.dir
162
163 - def create_path(self, prefix, suffix, folder = False, text = False, mode = "r+"):
164 """
165 Uses tempfile.mkdtemp and tempfile.mkstemp to create temporary
166 folders and files, respectively, under self.dir
167 """
168
169 if folder:
170 name = tempfile.mkdtemp(prefix = prefix, suffix = suffix, dir = self.dir)
171 self.logger.debug("Added folder %s", name)
172 else:
173 fd, name = tempfile.mkstemp(prefix = prefix, suffix = suffix, dir = self.dir, text = text)
174 self.logger.debug("Added file %s", name)
175 try:
176 os.close(fd)
177 except:
178 self.logger.warn("Failed to close fd %s" % fd)
179
180 return path(name)
181
183 """
184 If the given path is under self.dir, then it is deleted
185 whether file or folder. Otherwise, an exception is thrown.
186 """
187 p = path(name)
188 parpath = p.parpath(self.dir)
189 if len(parpath) < 1:
190 raise exceptions.Exception("%s is not in %s" % (p, self.dir))
191
192 if p.exists():
193 if p.isdir():
194 p.rmtree(onerror = self.on_rmtree)
195 self.logger.debug("Removed folder %s", name)
196 else:
197 p.remove()
198 self.logger.debug("Removed file %s", name)
199
208
210 """
211 Attempts to delete all directories under self.userdir
212 other than the one owned by this process. If a directory
213 is locked, it is skipped.
214 """
215 self.logger.debug("Cleaning user dir: %s" % self.userdir)
216 dirs = self.userdir.dirs()
217 for dir in dirs:
218 if str(dir) == str(self.dir):
219 self.logger.debug("Skipping self: %s", dir)
220 continue
221 lock = dir / ".lock"
222 f = open(str(lock),"r")
223 try:
224 portalocker.lock(f, portalocker.LOCK_EX|portalocker.LOCK_NB)
225 except:
226 print "Locked: %s" % dir
227 continue
228 dir.rmtree(self.on_rmtree)
229 print "Deleted: %s" % dir
230
232 self.logger.error("rmtree error: %s('%s') => %s", func.__name__, name, exc[1])
233
234 manager = TempFileManager()
235 """
236 Global TempFileManager instance for use by the current process and
237 registered with the atexit module for cleaning up all created files on exit.
238 Other instances can be created for specialized purposes.
239 """
240
241 -def create_path(prefix = "omero", suffix = ".tmp", folder = False):
242 """
243 Uses the global TempFileManager to create a temporary file.
244 """
245 return manager.create_path(prefix, suffix, folder = folder)
246
248 """
249 Removes the file from the global TempFileManager. The file will be deleted
250 if it still exists.
251 """
252 return manager.remove_path(file)
253
255 """
256 Returns the dir value for the global TempFileManager.
257 """
258 return manager.gettempdir()
259
260 if __name__ == "__main__":
261
262 from omero.util import configure_logging
263
264 if len(sys.argv) > 1:
265 args = sys.argv[1:]
266 else:
267 args = []
268
269 if "--debug" in args:
270 configure_logging(loglevel=logging.DEBUG)
271 else:
272 configure_logging()
273
274 if "clean" in args:
275 manager.clean_userdir()
276 sys.exit(0)
277 elif "dir" in args:
278 print manager.gettempdir()
279 sys.exit(0)
280 elif "lock" in args:
281 print "Locking %s" % manager.gettempdir()
282 raw_input("Waiting on user input...")
283 sys.exit(0)
284
285 print "Usage: %s clean" % sys.argv[0]
286 print " or: %s dir " % sys.argv[0]
287 sys.exit(2)
288