Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/lockfile/pidlockfile.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 | |
3 # pidlockfile.py | |
4 # | |
5 # Copyright © 2008–2009 Ben Finney <ben+python@benfinney.id.au> | |
6 # | |
7 # This is free software: you may copy, modify, and/or distribute this work | |
8 # under the terms of the Python Software Foundation License, version 2 or | |
9 # later as published by the Python Software Foundation. | |
10 # No warranty expressed or implied. See the file LICENSE.PSF-2 for details. | |
11 | |
12 """ Lockfile behaviour implemented via Unix PID files. | |
13 """ | |
14 | |
15 from __future__ import absolute_import | |
16 | |
17 import errno | |
18 import os | |
19 import time | |
20 | |
21 from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock, | |
22 LockTimeout) | |
23 | |
24 | |
25 class PIDLockFile(LockBase): | |
26 """ Lockfile implemented as a Unix PID file. | |
27 | |
28 The lock file is a normal file named by the attribute `path`. | |
29 A lock's PID file contains a single line of text, containing | |
30 the process ID (PID) of the process that acquired the lock. | |
31 | |
32 >>> lock = PIDLockFile('somefile') | |
33 >>> lock = PIDLockFile('somefile') | |
34 """ | |
35 | |
36 def __init__(self, path, threaded=False, timeout=None): | |
37 # pid lockfiles don't support threaded operation, so always force | |
38 # False as the threaded arg. | |
39 LockBase.__init__(self, path, False, timeout) | |
40 self.unique_name = self.path | |
41 | |
42 def read_pid(self): | |
43 """ Get the PID from the lock file. | |
44 """ | |
45 return read_pid_from_pidfile(self.path) | |
46 | |
47 def is_locked(self): | |
48 """ Test if the lock is currently held. | |
49 | |
50 The lock is held if the PID file for this lock exists. | |
51 | |
52 """ | |
53 return os.path.exists(self.path) | |
54 | |
55 def i_am_locking(self): | |
56 """ Test if the lock is held by the current process. | |
57 | |
58 Returns ``True`` if the current process ID matches the | |
59 number stored in the PID file. | |
60 """ | |
61 return self.is_locked() and os.getpid() == self.read_pid() | |
62 | |
63 def acquire(self, timeout=None): | |
64 """ Acquire the lock. | |
65 | |
66 Creates the PID file for this lock, or raises an error if | |
67 the lock could not be acquired. | |
68 """ | |
69 | |
70 timeout = timeout if timeout is not None else self.timeout | |
71 end_time = time.time() | |
72 if timeout is not None and timeout > 0: | |
73 end_time += timeout | |
74 | |
75 while True: | |
76 try: | |
77 write_pid_to_pidfile(self.path) | |
78 except OSError as exc: | |
79 if exc.errno == errno.EEXIST: | |
80 # The lock creation failed. Maybe sleep a bit. | |
81 if time.time() > end_time: | |
82 if timeout is not None and timeout > 0: | |
83 raise LockTimeout("Timeout waiting to acquire" | |
84 " lock for %s" % | |
85 self.path) | |
86 else: | |
87 raise AlreadyLocked("%s is already locked" % | |
88 self.path) | |
89 time.sleep(timeout is not None and timeout / 10 or 0.1) | |
90 else: | |
91 raise LockFailed("failed to create %s" % self.path) | |
92 else: | |
93 return | |
94 | |
95 def release(self): | |
96 """ Release the lock. | |
97 | |
98 Removes the PID file to release the lock, or raises an | |
99 error if the current process does not hold the lock. | |
100 | |
101 """ | |
102 if not self.is_locked(): | |
103 raise NotLocked("%s is not locked" % self.path) | |
104 if not self.i_am_locking(): | |
105 raise NotMyLock("%s is locked, but not by me" % self.path) | |
106 remove_existing_pidfile(self.path) | |
107 | |
108 def break_lock(self): | |
109 """ Break an existing lock. | |
110 | |
111 Removes the PID file if it already exists, otherwise does | |
112 nothing. | |
113 | |
114 """ | |
115 remove_existing_pidfile(self.path) | |
116 | |
117 | |
118 def read_pid_from_pidfile(pidfile_path): | |
119 """ Read the PID recorded in the named PID file. | |
120 | |
121 Read and return the numeric PID recorded as text in the named | |
122 PID file. If the PID file cannot be read, or if the content is | |
123 not a valid PID, return ``None``. | |
124 | |
125 """ | |
126 pid = None | |
127 try: | |
128 pidfile = open(pidfile_path, 'r') | |
129 except IOError: | |
130 pass | |
131 else: | |
132 # According to the FHS 2.3 section on PID files in /var/run: | |
133 # | |
134 # The file must consist of the process identifier in | |
135 # ASCII-encoded decimal, followed by a newline character. | |
136 # | |
137 # Programs that read PID files should be somewhat flexible | |
138 # in what they accept; i.e., they should ignore extra | |
139 # whitespace, leading zeroes, absence of the trailing | |
140 # newline, or additional lines in the PID file. | |
141 | |
142 line = pidfile.readline().strip() | |
143 try: | |
144 pid = int(line) | |
145 except ValueError: | |
146 pass | |
147 pidfile.close() | |
148 | |
149 return pid | |
150 | |
151 | |
152 def write_pid_to_pidfile(pidfile_path): | |
153 """ Write the PID in the named PID file. | |
154 | |
155 Get the numeric process ID (“PID”) of the current process | |
156 and write it to the named file as a line of text. | |
157 | |
158 """ | |
159 open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY) | |
160 open_mode = 0o644 | |
161 pidfile_fd = os.open(pidfile_path, open_flags, open_mode) | |
162 pidfile = os.fdopen(pidfile_fd, 'w') | |
163 | |
164 # According to the FHS 2.3 section on PID files in /var/run: | |
165 # | |
166 # The file must consist of the process identifier in | |
167 # ASCII-encoded decimal, followed by a newline character. For | |
168 # example, if crond was process number 25, /var/run/crond.pid | |
169 # would contain three characters: two, five, and newline. | |
170 | |
171 pid = os.getpid() | |
172 pidfile.write("%s\n" % pid) | |
173 pidfile.close() | |
174 | |
175 | |
176 def remove_existing_pidfile(pidfile_path): | |
177 """ Remove the named PID file if it exists. | |
178 | |
179 Removing a PID file that doesn't already exist puts us in the | |
180 desired state, so we ignore the condition if the file does not | |
181 exist. | |
182 | |
183 """ | |
184 try: | |
185 os.remove(pidfile_path) | |
186 except OSError as exc: | |
187 if exc.errno == errno.ENOENT: | |
188 pass | |
189 else: | |
190 raise |