OSDN Git Service

データを追加
[stux/ultron.git] / venv / Lib / site-packages / pip / _vendor / lockfile / pidlockfile.py
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 \f
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