| 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 |