1import py 2import sys, os, atexit 3 4 5# This is copied from PyPy's vendored py lib. The latest py lib release 6# (1.8.1) contains a bug and crashes if it sees another temporary directory 7# in which we don't have write permission (e.g. because it's owned by someone 8# else). 9def make_numbered_dir(prefix='session-', rootdir=None, keep=3, 10 lock_timeout = 172800, # two days 11 min_timeout = 300): # five minutes 12 """ return unique directory with a number greater than the current 13 maximum one. The number is assumed to start directly after prefix. 14 if keep is true directories with a number less than (maxnum-keep) 15 will be removed. 16 """ 17 if rootdir is None: 18 rootdir = py.path.local.get_temproot() 19 20 def parse_num(path): 21 """ parse the number out of a path (if it matches the prefix) """ 22 bn = path.basename 23 if bn.startswith(prefix): 24 try: 25 return int(bn[len(prefix):]) 26 except ValueError: 27 pass 28 29 # compute the maximum number currently in use with the 30 # prefix 31 lastmax = None 32 while True: 33 maxnum = -1 34 for path in rootdir.listdir(): 35 num = parse_num(path) 36 if num is not None: 37 maxnum = max(maxnum, num) 38 39 # make the new directory 40 try: 41 udir = rootdir.mkdir(prefix + str(maxnum+1)) 42 except py.error.EEXIST: 43 # race condition: another thread/process created the dir 44 # in the meantime. Try counting again 45 if lastmax == maxnum: 46 raise 47 lastmax = maxnum 48 continue 49 break 50 51 # put a .lock file in the new directory that will be removed at 52 # process exit 53 if lock_timeout: 54 lockfile = udir.join('.lock') 55 mypid = os.getpid() 56 if hasattr(lockfile, 'mksymlinkto'): 57 lockfile.mksymlinkto(str(mypid)) 58 else: 59 lockfile.write(str(mypid)) 60 def try_remove_lockfile(): 61 # in a fork() situation, only the last process should 62 # remove the .lock, otherwise the other processes run the 63 # risk of seeing their temporary dir disappear. For now 64 # we remove the .lock in the parent only (i.e. we assume 65 # that the children finish before the parent). 66 if os.getpid() != mypid: 67 return 68 try: 69 lockfile.remove() 70 except py.error.Error: 71 pass 72 atexit.register(try_remove_lockfile) 73 74 # prune old directories 75 if keep: 76 for path in rootdir.listdir(): 77 num = parse_num(path) 78 if num is not None and num <= (maxnum - keep): 79 if min_timeout: 80 # NB: doing this is needed to prevent (or reduce 81 # a lot the chance of) the following situation: 82 # 'keep+1' processes call make_numbered_dir() at 83 # the same time, they create dirs, but then the 84 # last process notices the first dir doesn't have 85 # (yet) a .lock in it and kills it. 86 try: 87 t1 = path.lstat().mtime 88 t2 = lockfile.lstat().mtime 89 if abs(t2-t1) < min_timeout: 90 continue # skip directories too recent 91 except py.error.Error: 92 continue # failure to get a time, better skip 93 lf = path.join('.lock') 94 try: 95 t1 = lf.lstat().mtime 96 t2 = lockfile.lstat().mtime 97 if not lock_timeout or abs(t2-t1) < lock_timeout: 98 continue # skip directories still locked 99 except py.error.Error: 100 pass # assume that it means that there is no 'lf' 101 try: 102 path.remove(rec=1) 103 except KeyboardInterrupt: 104 raise 105 except: # this might be py.error.Error, WindowsError ... 106 pass 107 108 # make link... 109 try: 110 username = os.environ['USER'] #linux, et al 111 except KeyError: 112 try: 113 username = os.environ['USERNAME'] #windows 114 except KeyError: 115 username = 'current' 116 117 src = str(udir) 118 dest = src[:src.rfind('-')] + '-' + username 119 try: 120 os.unlink(dest) 121 except OSError: 122 pass 123 try: 124 os.symlink(src, dest) 125 except (OSError, AttributeError, NotImplementedError): 126 pass 127 128 return udir 129 130 131udir = make_numbered_dir(prefix = 'ffi-') 132 133 134# Windows-only workaround for some configurations: see 135# https://bugs.python.org/issue23246 (Python 2.7.9) 136if sys.platform == 'win32': 137 try: 138 import setuptools 139 except ImportError: 140 pass 141