comparison planemo/lib/python3.7/site-packages/psutil/_psposix.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Routines common to all posix systems."""
6
7 import glob
8 import os
9 import signal
10 import sys
11 import time
12
13 from ._common import memoize
14 from ._common import sdiskusage
15 from ._common import TimeoutExpired
16 from ._common import usage_percent
17 from ._compat import ChildProcessError
18 from ._compat import FileNotFoundError
19 from ._compat import InterruptedError
20 from ._compat import PermissionError
21 from ._compat import ProcessLookupError
22 from ._compat import PY3
23 from ._compat import unicode
24
25 if sys.version_info >= (3, 4):
26 import enum
27 else:
28 enum = None
29
30
31 __all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
32
33
34 def pid_exists(pid):
35 """Check whether pid exists in the current process table."""
36 if pid == 0:
37 # According to "man 2 kill" PID 0 has a special meaning:
38 # it refers to <<every process in the process group of the
39 # calling process>> so we don't want to go any further.
40 # If we get here it means this UNIX platform *does* have
41 # a process with id 0.
42 return True
43 try:
44 os.kill(pid, 0)
45 except ProcessLookupError:
46 return False
47 except PermissionError:
48 # EPERM clearly means there's a process to deny access to
49 return True
50 # According to "man 2 kill" possible error values are
51 # (EINVAL, EPERM, ESRCH)
52 else:
53 return True
54
55
56 # Python 3.5 signals enum (contributed by me ^^):
57 # https://bugs.python.org/issue21076
58 if enum is not None and hasattr(signal, "Signals"):
59 Negsignal = enum.IntEnum(
60 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals]))
61
62 def negsig_to_enum(num):
63 """Convert a negative signal value to an enum."""
64 try:
65 return Negsignal(num)
66 except ValueError:
67 return num
68 else:
69 def negsig_to_enum(num):
70 return num
71
72
73 def wait_pid(pid, timeout=None, proc_name=None,
74 _waitpid=os.waitpid,
75 _timer=getattr(time, 'monotonic', time.time),
76 _min=min,
77 _sleep=time.sleep,
78 _pid_exists=pid_exists):
79 """Wait for a process PID to terminate.
80
81 If the process terminated normally by calling exit(3) or _exit(2),
82 or by returning from main(), the return value is the positive integer
83 passed to *exit().
84
85 If it was terminated by a signal it returns the negated value of the
86 signal which caused the termination (e.g. -SIGTERM).
87
88 If PID is not a children of os.getpid() (current process) just
89 wait until the process disappears and return None.
90
91 If PID does not exist at all return None immediately.
92
93 If *timeout* != None and process is still alive raise TimeoutExpired.
94 timeout=0 is also possible (either return immediately or raise).
95 """
96 if pid <= 0:
97 raise ValueError("can't wait for PID 0") # see "man waitpid"
98 interval = 0.0001
99 flags = 0
100 if timeout is not None:
101 flags |= os.WNOHANG
102 stop_at = _timer() + timeout
103
104 def sleep(interval):
105 # Sleep for some time and return a new increased interval.
106 if timeout is not None:
107 if _timer() >= stop_at:
108 raise TimeoutExpired(timeout, pid=pid, name=proc_name)
109 _sleep(interval)
110 return _min(interval * 2, 0.04)
111
112 # See: https://linux.die.net/man/2/waitpid
113 while True:
114 try:
115 retpid, status = os.waitpid(pid, flags)
116 except InterruptedError:
117 interval = sleep(interval)
118 except ChildProcessError:
119 # This has two meanings:
120 # - PID is not a child of os.getpid() in which case
121 # we keep polling until it's gone
122 # - PID never existed in the first place
123 # In both cases we'll eventually return None as we
124 # can't determine its exit status code.
125 while _pid_exists(pid):
126 interval = sleep(interval)
127 return
128 else:
129 if retpid == 0:
130 # WNOHANG flag was used and PID is still running.
131 interval = sleep(interval)
132 continue
133 elif os.WIFEXITED(status):
134 # Process terminated normally by calling exit(3) or _exit(2),
135 # or by returning from main(). The return value is the
136 # positive integer passed to *exit().
137 return os.WEXITSTATUS(status)
138 elif os.WIFSIGNALED(status):
139 # Process exited due to a signal. Return the negative value
140 # of that signal.
141 return negsig_to_enum(-os.WTERMSIG(status))
142 # elif os.WIFSTOPPED(status):
143 # # Process was stopped via SIGSTOP or is being traced, and
144 # # waitpid() was called with WUNTRACED flag. PID is still
145 # # alive. From now on waitpid() will keep returning (0, 0)
146 # # until the process state doesn't change.
147 # # It may make sense to catch/enable this since stopped PIDs
148 # # ignore SIGTERM.
149 # interval = sleep(interval)
150 # continue
151 # elif os.WIFCONTINUED(status):
152 # # Process was resumed via SIGCONT and waitpid() was called
153 # # with WCONTINUED flag.
154 # interval = sleep(interval)
155 # continue
156 else:
157 # Should never happen.
158 raise ValueError("unknown process exit status %r" % status)
159
160
161 def disk_usage(path):
162 """Return disk usage associated with path.
163 Note: UNIX usually reserves 5% disk space which is not accessible
164 by user. In this function "total" and "used" values reflect the
165 total and used disk space whereas "free" and "percent" represent
166 the "free" and "used percent" user disk space.
167 """
168 if PY3:
169 st = os.statvfs(path)
170 else:
171 # os.statvfs() does not support unicode on Python 2:
172 # - https://github.com/giampaolo/psutil/issues/416
173 # - http://bugs.python.org/issue18695
174 try:
175 st = os.statvfs(path)
176 except UnicodeEncodeError:
177 if isinstance(path, unicode):
178 try:
179 path = path.encode(sys.getfilesystemencoding())
180 except UnicodeEncodeError:
181 pass
182 st = os.statvfs(path)
183 else:
184 raise
185
186 # Total space which is only available to root (unless changed
187 # at system level).
188 total = (st.f_blocks * st.f_frsize)
189 # Remaining free space usable by root.
190 avail_to_root = (st.f_bfree * st.f_frsize)
191 # Remaining free space usable by user.
192 avail_to_user = (st.f_bavail * st.f_frsize)
193 # Total space being used in general.
194 used = (total - avail_to_root)
195 # Total space which is available to user (same as 'total' but
196 # for the user).
197 total_user = used + avail_to_user
198 # User usage percent compared to the total amount of space
199 # the user can use. This number would be higher if compared
200 # to root's because the user has less space (usually -5%).
201 usage_percent_user = usage_percent(used, total_user, round_=1)
202
203 # NB: the percentage is -5% than what shown by df due to
204 # reserved blocks that we are currently not considering:
205 # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462
206 return sdiskusage(
207 total=total, used=used, free=avail_to_user, percent=usage_percent_user)
208
209
210 @memoize
211 def get_terminal_map():
212 """Get a map of device-id -> path as a dict.
213 Used by Process.terminal()
214 """
215 ret = {}
216 ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
217 for name in ls:
218 assert name not in ret, name
219 try:
220 ret[os.stat(name).st_rdev] = name
221 except FileNotFoundError:
222 pass
223 return ret