1 """!Change directory, handle temporary directories
3 This module provides a means by which to change to a different
4 directory in a Python "with" block and change back out afterwards,
5 regardless of what happens inside the block. It can, optionally,
6 create a new directory, and optionally delete it at the end of the
7 block. There are two classes:
9 * TempDir - creates a temporary directory with a randomly-generated
10 name, chdirs to the directory, and chdirs back out afterwards.
11 It can be configured to delete the directory afterwards (the
14 * NamedDir - a subclass of TempDir that uses a specific directory
15 rather than a randomly-generated one. By default, the directory
16 is NOT deleted at the end of the block. That can be configured."""
18 import tempfile, os, re, logging, sys, shutil, errno, stat
23 __all__=[
'TempDir',
'NamedDir',
'perm_remove',
'perm_add']
29 perm_add = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \
30 stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH
36 perm_remove = stat.S_IWOTH|stat.S_ISUID
39 """!This class is intended to be used with the Python "with TempDir() as t" syntax.
43 # we're now in the temporary directory
45 # the temporary directory has been deleted now
48 def __init__(self,suffix='.tmp',prefix='tempdir.',dir=None,keep=False,
49 logger=
None,print_on_exception=
True,add_perms=perm_add,
50 remove_perms=perm_remove,keep_on_error=
True,cd=
True):
51 """!Creates a TempDir.
53 @param suffix,prefix,dir Passed to the tempfile.mkdtemp to
54 generate the directory name. Meanings are the same as in
55 that constructor. See the Python documentation for
57 @param keep Controls directory deletion if the "with" block
58 returns without an exception. If False, the directory is
59 deleted. Default: keep=False
60 @param logger A logging.logger for log messages
61 @param print_on_exception Print exceptions before leaving the dir
62 @param add_perms Permissions to add to the directory. Default: 755.
63 @param remove_perms Permissions to remove from the directory.
64 Default: setuid and world write.
65 @param keep_on_error Controls directory deletion if the "with"
66 block raises an Exception or GeneratorExit, or subclass
67 thereof. If False, the directory is deleted under those
68 circumstances. Default: keep_on_error=True.
69 @param cd If True (default), cd to the directory in the "with"
70 block and cd back out afterwards. If False, then only
71 directory creation and deletion happens. """
86 assert(dir
is not None)
87 if not os.path.isabs(self.
dir):
88 self.
dir=os.path.join(os.getcwd(),self.
dir)
89 assert(os.path.isabs(self.
dir))
109 """!Decide the name of the directory, and create the directory.
110 Also create any path components leading up to the
112 if self.
prefix is not None:
114 d=os.path.dirname(self.
prefix)
115 if d
is not None and d!=
'':
117 except EnvironmentError
as e:
118 if e.errno != errno.EEXIST:
127 """!Creates the temporary directory and chdirs the current
128 process into that directory. It calls self.name_make_dir() to
129 do the naming and directory creation."""
134 self._logger.info(
'chdir to temporary directory '+self.
dirname)
136 """!Exit the temporary directory created by mkdir_cd and
137 return to the original directory, if possible."""
139 self._logger.info(
'chdir to old directory '+self.
olddir)
142 """!CD out and remove the old directory.
144 This subroutine exits the temporary directory created by
145 mkdir_cd, and then deletes that temporary directory. After
146 this routine, the process will be in its original directory
147 (from before the call to mkdir_cd) if possible, or otherwise
148 it will be in the root directory (/).
150 It is the caller's responsibility to ensure this function is
151 not called if keep_on_error=True and an error occurs."""
154 except(Exception)
as e:
156 self._logger.critical(
'could not chdir, so chdir to root '
157 'because of exception: '+repr(e))
158 if self.
_cd: os.chdir(
'/')
161 and os.path.isabs(self.
dirname):
163 self._logger.info(
'%s: delete temporary directory'
168 shutil.rmtree(self.
dirname,
True)
170 self._logger.warning(
'%s: could not delete directory'
173 self._logger.info(
'%s: not deleting temporary directory'
176 def _rmerror(self,function,path,excinfo):
177 """!Called when a file removal error happens.
178 @param function,path,excinfo exception information"""
180 self.
_logger(
'%s: cannot remove'%(str(path),),exc_info=excinfo)
182 """!Called to dump information to a log, or failing that, the
183 terminal if an unexpected exception is caught."""
185 self._logger.warning(self.
dirname
186 +
': leaving temp directory due to exception')
187 self._logger.info(
'%s: listing current directory (%s) via ls -l',
190 logging.getLogger(
'produtil.tempdir').warning(
191 self.
dirname+
': leaving temp directory due to exception')
194 """!This is a simple wrapper around mkdir_cd that is intended
195 to be used with in a "with" block. This subroutine is
196 automatically called at the beginning of the block."""
200 """!Exit the 'with' block.
202 This is a simple wrapper around cd_rmdir that is intended to
203 be used with in a "with" block. This subroutine is
204 automatically called at the end of the block. It will call
205 cd_rmdir to delete the directory unless an exception is thrown
206 that is NOT a subclass of Exception or GeneratorExit. The
207 removal is skipped to allow the program to exit quickly in
208 case of a fatal signal (ie.: SIGQUIT, SIGTERM, SIGINT,
210 @param etype,value,traceback exception information"""
211 if value
is None or isinstance(value,GeneratorExit):
214 elif isinstance(value,Exception):
229 """!This subclass of TempDir takes a directory name, instead of
230 generating one automatically. By default, it will NOT delete the
231 directory upon __exit__. That can be overridden by specifying
233 def __init__(self,dirname,keep=True,logger=None,keep_on_error=True,
234 add_perms=0,remove_perms=0,rm_first=
False):
235 """!Create a NamedDir for the specified directory. The given
236 logger is used to log messages. There are two deletion
237 vs. non-deletion options:
239 @param dirname The directory name
240 @param keep If False, the file is deleted upon successful return
241 of the "with" block. If True, the file is kept upon
243 @param logger A logging.logger for log messages
244 @param add_perms Permissions to add to the directory after cding
245 into it. Default: none.
246 @param remove_perms Permissions to remove from the directory after
247 cding into it. Default: none.
248 @param keep_on_error Controls deletion upon catching of an
249 Exception or GeneratorException (or subclass thereof).
250 @param rm_first If the directory already exists, delete it first
251 and make a new one before cding to it."""
252 if not isinstance(dirname,basestring):
254 'NamedDir requires a string name as its first argument.')
256 keep=keep,logger=logger,keep_on_error=keep_on_error,
257 add_perms=add_perms,remove_perms=remove_perms)
263 """!Replacement for the TempDir.name_make_dir. Uses the
264 directory name specified in the constructor."""
265 if not self.
_cd:
return
267 if os.path.exists(self.
dirname):
271 shutil.rmtree(self.
dirname,
True)
272 if not os.path.exists(self.
dirname):
Imitates the shell "ls -l" program.
def __exit__(self, etype, value, traceback)
Exit the 'with' block.
print_on_exception
Should we print exceptions before exiting the directory?
def cd_out(self)
Exit the temporary directory created by mkdir_cd and return to the original directory, if possible.
prefix
Temporary directory name prefix.
def cd_rmdir(self)
CD out and remove the old directory.
olddir
The name of the directory we came from.
def _rmerror(self, function, path, excinfo)
Called when a file removal error happens.
This subclass of TempDir takes a directory name, instead of generating one automatically.
def __init__
Create a NamedDir for the specified directory.
suffix
Temporary directory name suffix.
This class is intended to be used with the Python "with TempDir() as t" syntax.
def name_make_dir(self)
Decide the name of the directory, and create the directory.
Contains the Listing class, which emulates "ls -l".
def __init__
Creates a TempDir.
def __enter__(self)
This is a simple wrapper around mkdir_cd that is intended to be used with in a "with" block...
def exception_info(self)
Called to dump information to a log, or failing that, the terminal if an unexpected exception is caug...
def name_make_dir(self)
Replacement for the TempDir.name_make_dir.
dirname
The name of the target directory.
def mkdir_cd(self)
Creates the temporary directory and chdirs the current process into that directory.