• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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