2 Futz with files like a pro.
5 import sys, exceptions, os, stat, tempfile, time
7 from twisted.python import log
9 def rename(src, dst, tries=4, basedelay=0.1):
10 """ Here is a superkludge to workaround the fact that occasionally on
11 Windows some other process (e.g. an anti-virus scanner, a local search
12 engine, etc.) is looking at your file when you want to delete or move it,
13 and hence you can't. The horrible workaround is to sit and spin, trying
14 to delete it, for a short time and then give up.
16 With the default values of tries and basedelay this can block for less
19 @param tries: number of tries -- each time after the first we wait twice
20 as long as the previous wait
21 @param basedelay: how long to wait before the second try
23 for i in range(tries-1):
25 return os.rename(src, dst)
26 except EnvironmentError, le:
27 # XXX Tighten this to check if this is a permission denied error (possibly due to another Windows process having the file open and execute the superkludge only in this case.
28 log.msg("XXX KLUDGE Attempting to move file %s => %s; got %s; sleeping %s seconds" % (src, dst, le, basedelay,))
31 return os.rename(src, dst) # The last try.
33 def remove(f, tries=4, basedelay=0.1):
34 """ Here is a superkludge to workaround the fact that occasionally on
35 Windows some other process (e.g. an anti-virus scanner, a local search
36 engine, etc.) is looking at your file when you want to delete or move it,
37 and hence you can't. The horrible workaround is to sit and spin, trying
38 to delete it, for a short time and then give up.
40 With the default values of tries and basedelay this can block for less
43 @param tries: number of tries -- each time after the first we wait twice
44 as long as the previous wait
45 @param basedelay: how long to wait before the second try
48 os.chmod(f, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
51 for i in range(tries-1):
54 except EnvironmentError, le:
55 # XXX Tighten this to check if this is a permission denied error (possibly due to another Windows process having the file open and execute the superkludge only in this case.
56 if not os.path.exists(f):
58 log.msg("XXX KLUDGE Attempting to remove file %s; got %s; sleeping %s seconds" % (f, le, basedelay,))
61 return os.remove(f) # The last try.
63 class ReopenableNamedTemporaryFile:
65 This uses tempfile.mkstemp() to generate a secure temp file. It then closes
66 the file, leaving a zero-length file as a placeholder. You can get the
67 filename with ReopenableNamedTemporaryFile.name. When the
68 ReopenableNamedTemporaryFile instance is garbage collected or its shutdown()
69 method is called, it deletes the file.
71 def __init__(self, *args, **kwargs):
72 fd, self.name = tempfile.mkstemp(*args, **kwargs)
76 return "<%s instance at %x %s>" % (self.__class__.__name__, id(self), self.name)
79 return self.__repr__()
87 class NamedTemporaryDirectory:
89 This calls tempfile.mkdtemp(), stores the name of the dir in
90 self.name, and rmrf's the dir when it gets garbage collected or
93 def __init__(self, cleanup=True, *args, **kwargs):
94 """ If cleanup, then the directory will be rmrf'ed when the object is shutdown. """
95 self.cleanup = cleanup
96 self.name = tempfile.mkdtemp(*args, **kwargs)
99 return "<%s instance at %x %s>" % (self.__class__.__name__, id(self), self.name)
102 return self.__repr__()
109 traceback.print_exc()
112 if self.cleanup and hasattr(self, 'name'):
115 def make_dirs(dirname, mode=0777):
117 An idempotent version of os.makedirs(). If the dir already exists, do
118 nothing and return without raising an exception. If this call creates the
119 dir, return without raising an exception. If there is an error that
120 prevents creation or if the directory gets deleted after make_dirs() creates
121 it and before make_dirs() checks that it exists, raise an exception.
125 os.makedirs(dirname, mode)
129 if not os.path.isdir(dirname):
132 raise exceptions.IOError, "unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname # careful not to construct an IOError with a 2-tuple, as that has a special meaning...
136 A threadsafe and idempotent version of shutil.rmtree(). If the dir is
137 already gone, do nothing and return without raising an exception. If this
138 call removes the dir, return without raising an exception. If there is an
139 error that prevents deletion or if the directory gets created again after
140 rm_dir() deletes it and before rm_dir() checks that it is gone, raise an
145 os.chmod(dirname, stat.S_IWRITE | stat.S_IEXEC | stat.S_IREAD)
146 for f in os.listdir(dirname):
147 fullname = os.path.join(dirname, f)
148 if os.path.isdir(fullname):
153 except Exception, le:
154 # Ignore "No such file or directory"
155 if (not isinstance(le, OSError)) or le.args[0] != 2:
158 # Okay, now we've recursively removed everything, ignoring any "No
159 # such file or directory" errors, and collecting any other errors.
161 if os.path.exists(dirname):
165 raise OSError, "Failed to remove dir for unknown reason."
169 def remove_if_possible(f):
175 def open_or_create(fname, binarymode=True):
177 return open(fname, binarymode and "r+b" or "r+")
178 except EnvironmentError:
179 return open(fname, binarymode and "w+b" or "w+")
184 for root, dirs, files in os.walk(basedir):
186 fn = os.path.join(root, f)
187 size += os.path.getsize(fn)
191 def move_into_place(source, dest):
192 """Atomically replace a file, or as near to it as the platform allows.
193 The dest file may or may not exist."""
194 if "win32" in sys.platform.lower():
195 remove_if_possible(dest)
196 os.rename(source, dest)
198 def write(path, data):
199 wf = open(path, "wb")
206 rf = open(path, "rb")
212 def put_file(pathname, inf):
213 # TODO: create temporary file and move into place?
214 outf = open(os.path.expanduser(pathname), "wb")
217 data = inf.read(32768)