Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/lockfile/pidlockfile.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
| author | shellac |
|---|---|
| date | Mon, 01 Jun 2020 08:59:25 -0400 |
| parents | 79f47841a781 |
| children |
comparison
equal
deleted
inserted
replaced
| 4:79f47841a781 | 5:9b1c78e6ba9c |
|---|---|
| 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 |
