import py import sys, os, atexit # This is copied from PyPy's vendored py lib. The latest py lib release # (1.8.1) contains a bug and crashes if it sees another temporary directory # in which we don't have write permission (e.g. because it's owned by someone # else). def make_numbered_dir(prefix='session-', rootdir=None, keep=3, lock_timeout = 172800, # two days min_timeout = 300): # five minutes """ return unique directory with a number greater than the current maximum one. The number is assumed to start directly after prefix. if keep is true directories with a number less than (maxnum-keep) will be removed. """ if rootdir is None: rootdir = py.path.local.get_temproot() def parse_num(path): """ parse the number out of a path (if it matches the prefix) """ bn = path.basename if bn.startswith(prefix): try: return int(bn[len(prefix):]) except ValueError: pass # compute the maximum number currently in use with the # prefix lastmax = None while True: maxnum = -1 for path in rootdir.listdir(): num = parse_num(path) if num is not None: maxnum = max(maxnum, num) # make the new directory try: udir = rootdir.mkdir(prefix + str(maxnum+1)) except py.error.EEXIST: # race condition: another thread/process created the dir # in the meantime. Try counting again if lastmax == maxnum: raise lastmax = maxnum continue break # put a .lock file in the new directory that will be removed at # process exit if lock_timeout: lockfile = udir.join('.lock') mypid = os.getpid() if hasattr(lockfile, 'mksymlinkto'): lockfile.mksymlinkto(str(mypid)) else: lockfile.write(str(mypid)) def try_remove_lockfile(): # in a fork() situation, only the last process should # remove the .lock, otherwise the other processes run the # risk of seeing their temporary dir disappear. For now # we remove the .lock in the parent only (i.e. we assume # that the children finish before the parent). if os.getpid() != mypid: return try: lockfile.remove() except py.error.Error: pass atexit.register(try_remove_lockfile) # prune old directories if keep: for path in rootdir.listdir(): num = parse_num(path) if num is not None and num <= (maxnum - keep): if min_timeout: # NB: doing this is needed to prevent (or reduce # a lot the chance of) the following situation: # 'keep+1' processes call make_numbered_dir() at # the same time, they create dirs, but then the # last process notices the first dir doesn't have # (yet) a .lock in it and kills it. try: t1 = path.lstat().mtime t2 = lockfile.lstat().mtime if abs(t2-t1) < min_timeout: continue # skip directories too recent except py.error.Error: continue # failure to get a time, better skip lf = path.join('.lock') try: t1 = lf.lstat().mtime t2 = lockfile.lstat().mtime if not lock_timeout or abs(t2-t1) < lock_timeout: continue # skip directories still locked except py.error.Error: pass # assume that it means that there is no 'lf' try: path.remove(rec=1) except KeyboardInterrupt: raise except: # this might be py.error.Error, WindowsError ... pass # make link... try: username = os.environ['USER'] #linux, et al except KeyError: try: username = os.environ['USERNAME'] #windows except KeyError: username = 'current' src = str(udir) dest = src[:src.rfind('-')] + '-' + username try: os.unlink(dest) except OSError: pass try: os.symlink(src, dest) except (OSError, AttributeError, NotImplementedError): pass return udir udir = make_numbered_dir(prefix = 'ffi-') # Windows-only workaround for some configurations: see # https://bugs.python.org/issue23246 (Python 2.7.9) if sys.platform == 'win32': try: import setuptools except ImportError: pass