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