mirror of https://github.com/tp4a/teleport
.temp.macos.
parent
f78fc3b4fe
commit
04309f8fbf
|
@ -17,7 +17,7 @@ sensors) in Python. Supported platforms:
|
||||||
- Sun Solaris
|
- Sun Solaris
|
||||||
- AIX
|
- AIX
|
||||||
|
|
||||||
Works with Python versions from 2.6 to 3.X.
|
Works with Python versions from 2.6 to 3.4+.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
@ -31,8 +31,8 @@ import os
|
||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
import traceback
|
|
||||||
try:
|
try:
|
||||||
import pwd
|
import pwd
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -87,12 +87,6 @@ from ._common import POSIX # NOQA
|
||||||
from ._common import SUNOS
|
from ._common import SUNOS
|
||||||
from ._common import WINDOWS
|
from ._common import WINDOWS
|
||||||
|
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import Error
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import TimeoutExpired
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
if LINUX:
|
if LINUX:
|
||||||
# This is public API and it will be retrieved from _pslinux.py
|
# This is public API and it will be retrieved from _pslinux.py
|
||||||
# via sys.modules.
|
# via sys.modules.
|
||||||
|
@ -152,6 +146,10 @@ elif WINDOWS:
|
||||||
from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
|
from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
|
||||||
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
|
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
|
||||||
from ._pswindows import CONN_DELETE_TCB # NOQA
|
from ._pswindows import CONN_DELETE_TCB # NOQA
|
||||||
|
from ._pswindows import IOPRIO_VERYLOW # NOQA
|
||||||
|
from ._pswindows import IOPRIO_LOW # NOQA
|
||||||
|
from ._pswindows import IOPRIO_NORMAL # NOQA
|
||||||
|
from ._pswindows import IOPRIO_HIGH # NOQA
|
||||||
|
|
||||||
elif MACOS:
|
elif MACOS:
|
||||||
from . import _psosx as _psplatform
|
from . import _psosx as _psplatform
|
||||||
|
@ -212,23 +210,26 @@ __all__ = [
|
||||||
"pid_exists", "pids", "process_iter", "wait_procs", # proc
|
"pid_exists", "pids", "process_iter", "wait_procs", # proc
|
||||||
"virtual_memory", "swap_memory", # memory
|
"virtual_memory", "swap_memory", # memory
|
||||||
"cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
|
"cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
|
||||||
"cpu_stats", # "cpu_freq",
|
"cpu_stats", # "cpu_freq", "getloadavg"
|
||||||
"net_io_counters", "net_connections", "net_if_addrs", # network
|
"net_io_counters", "net_connections", "net_if_addrs", # network
|
||||||
"net_if_stats",
|
"net_if_stats",
|
||||||
"disk_io_counters", "disk_partitions", "disk_usage", # disk
|
"disk_io_counters", "disk_partitions", "disk_usage", # disk
|
||||||
# "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
|
# "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
|
||||||
"users", "boot_time", # others
|
"users", "boot_time", # others
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
__all__.extend(_psplatform.__extra__all__)
|
__all__.extend(_psplatform.__extra__all__)
|
||||||
__author__ = "Giampaolo Rodola'"
|
__author__ = "Giampaolo Rodola'"
|
||||||
__version__ = "5.4.7"
|
__version__ = "5.6.3"
|
||||||
version_info = tuple([int(num) for num in __version__.split('.')])
|
version_info = tuple([int(num) for num in __version__.split('.')])
|
||||||
|
|
||||||
|
_timer = getattr(time, 'monotonic', time.time)
|
||||||
AF_LINK = _psplatform.AF_LINK
|
AF_LINK = _psplatform.AF_LINK
|
||||||
POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
|
POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
|
||||||
POWER_TIME_UNKNOWN = _common.POWER_TIME_UNKNOWN
|
POWER_TIME_UNKNOWN = _common.POWER_TIME_UNKNOWN
|
||||||
_TOTAL_PHYMEM = None
|
_TOTAL_PHYMEM = None
|
||||||
_timer = getattr(time, 'monotonic', time.time)
|
_LOWEST_PID = None
|
||||||
|
|
||||||
|
|
||||||
# Sanity check in case the user messed up with psutil installation
|
# Sanity check in case the user messed up with psutil installation
|
||||||
# or did something weird with sys.path. In this case we might end
|
# or did something weird with sys.path. In this case we might end
|
||||||
|
@ -252,6 +253,112 @@ if (int(__version__.replace('.', '')) !=
|
||||||
raise ImportError(msg)
|
raise ImportError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# --- Exceptions
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
"""Base exception class. All other psutil exceptions inherit
|
||||||
|
from this one.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, msg=""):
|
||||||
|
Exception.__init__(self, msg)
|
||||||
|
self.msg = msg
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
|
||||||
|
return ret.strip()
|
||||||
|
|
||||||
|
__str__ = __repr__
|
||||||
|
|
||||||
|
|
||||||
|
class NoSuchProcess(Error):
|
||||||
|
"""Exception raised when a process with a certain PID doesn't
|
||||||
|
or no longer exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, pid, name=None, msg=None):
|
||||||
|
Error.__init__(self, msg)
|
||||||
|
self.pid = pid
|
||||||
|
self.name = name
|
||||||
|
self.msg = msg
|
||||||
|
if msg is None:
|
||||||
|
if name:
|
||||||
|
details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
|
||||||
|
else:
|
||||||
|
details = "(pid=%s)" % self.pid
|
||||||
|
self.msg = "process no longer exists " + details
|
||||||
|
|
||||||
|
|
||||||
|
class ZombieProcess(NoSuchProcess):
|
||||||
|
"""Exception raised when querying a zombie process. This is
|
||||||
|
raised on macOS, BSD and Solaris only, and not always: depending
|
||||||
|
on the query the OS may be able to succeed anyway.
|
||||||
|
On Linux all zombie processes are querable (hence this is never
|
||||||
|
raised). Windows doesn't have zombie processes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, pid, name=None, ppid=None, msg=None):
|
||||||
|
NoSuchProcess.__init__(self, msg)
|
||||||
|
self.pid = pid
|
||||||
|
self.ppid = ppid
|
||||||
|
self.name = name
|
||||||
|
self.msg = msg
|
||||||
|
if msg is None:
|
||||||
|
args = ["pid=%s" % pid]
|
||||||
|
if name:
|
||||||
|
args.append("name=%s" % repr(self.name))
|
||||||
|
if ppid:
|
||||||
|
args.append("ppid=%s" % self.ppid)
|
||||||
|
details = "(%s)" % ", ".join(args)
|
||||||
|
self.msg = "process still exists but it's a zombie " + details
|
||||||
|
|
||||||
|
|
||||||
|
class AccessDenied(Error):
|
||||||
|
"""Exception raised when permission to perform an action is denied."""
|
||||||
|
|
||||||
|
def __init__(self, pid=None, name=None, msg=None):
|
||||||
|
Error.__init__(self, msg)
|
||||||
|
self.pid = pid
|
||||||
|
self.name = name
|
||||||
|
self.msg = msg
|
||||||
|
if msg is None:
|
||||||
|
if (pid is not None) and (name is not None):
|
||||||
|
self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
|
||||||
|
elif (pid is not None):
|
||||||
|
self.msg = "(pid=%s)" % self.pid
|
||||||
|
else:
|
||||||
|
self.msg = ""
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutExpired(Error):
|
||||||
|
"""Raised on Process.wait(timeout) if timeout expires and process
|
||||||
|
is still alive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, seconds, pid=None, name=None):
|
||||||
|
Error.__init__(self, "timeout after %s seconds" % seconds)
|
||||||
|
self.seconds = seconds
|
||||||
|
self.pid = pid
|
||||||
|
self.name = name
|
||||||
|
if (pid is not None) and (name is not None):
|
||||||
|
self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
|
||||||
|
elif (pid is not None):
|
||||||
|
self.msg += " (pid=%s)" % self.pid
|
||||||
|
|
||||||
|
|
||||||
|
# Push exception classes into platform specific module namespace.
|
||||||
|
_psplatform.NoSuchProcess = NoSuchProcess
|
||||||
|
_psplatform.ZombieProcess = ZombieProcess
|
||||||
|
_psplatform.AccessDenied = AccessDenied
|
||||||
|
_psplatform.TimeoutExpired = TimeoutExpired
|
||||||
|
if POSIX:
|
||||||
|
from . import _psposix
|
||||||
|
_psposix.TimeoutExpired = TimeoutExpired
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- Utils
|
# --- Utils
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -353,7 +460,7 @@ class Process(object):
|
||||||
self._create_time = None
|
self._create_time = None
|
||||||
self._gone = False
|
self._gone = False
|
||||||
self._hash = None
|
self._hash = None
|
||||||
self._oneshot_inctx = False
|
self._lock = threading.RLock()
|
||||||
# used for caching on Windows only (on POSIX ppid may change)
|
# used for caching on Windows only (on POSIX ppid may change)
|
||||||
self._ppid = None
|
self._ppid = None
|
||||||
# platform-specific modules define an _psplatform.Process
|
# platform-specific modules define an _psplatform.Process
|
||||||
|
@ -457,40 +564,45 @@ class Process(object):
|
||||||
...
|
...
|
||||||
>>>
|
>>>
|
||||||
"""
|
"""
|
||||||
if self._oneshot_inctx:
|
with self._lock:
|
||||||
# NOOP: this covers the use case where the user enters the
|
if hasattr(self, "_cache"):
|
||||||
# context twice. Since as_dict() internally uses oneshot()
|
# NOOP: this covers the use case where the user enters the
|
||||||
# I expect that the code below will be a pretty common
|
# context twice:
|
||||||
# "mistake" that the user will make, so let's guard
|
#
|
||||||
# against that:
|
# >>> with p.oneshot():
|
||||||
#
|
# ... with p.oneshot():
|
||||||
# >>> with p.oneshot():
|
# ...
|
||||||
# ... p.as_dict()
|
#
|
||||||
# ...
|
# Also, since as_dict() internally uses oneshot()
|
||||||
yield
|
# I expect that the code below will be a pretty common
|
||||||
else:
|
# "mistake" that the user will make, so let's guard
|
||||||
self._oneshot_inctx = True
|
# against that:
|
||||||
try:
|
#
|
||||||
# cached in case cpu_percent() is used
|
# >>> with p.oneshot():
|
||||||
self.cpu_times.cache_activate()
|
# ... p.as_dict()
|
||||||
# cached in case memory_percent() is used
|
# ...
|
||||||
self.memory_info.cache_activate()
|
|
||||||
# cached in case parent() is used
|
|
||||||
self.ppid.cache_activate()
|
|
||||||
# cached in case username() is used
|
|
||||||
if POSIX:
|
|
||||||
self.uids.cache_activate()
|
|
||||||
# specific implementation cache
|
|
||||||
self._proc.oneshot_enter()
|
|
||||||
yield
|
yield
|
||||||
finally:
|
else:
|
||||||
self.cpu_times.cache_deactivate()
|
try:
|
||||||
self.memory_info.cache_deactivate()
|
# cached in case cpu_percent() is used
|
||||||
self.ppid.cache_deactivate()
|
self.cpu_times.cache_activate(self)
|
||||||
if POSIX:
|
# cached in case memory_percent() is used
|
||||||
self.uids.cache_deactivate()
|
self.memory_info.cache_activate(self)
|
||||||
self._proc.oneshot_exit()
|
# cached in case parent() is used
|
||||||
self._oneshot_inctx = False
|
self.ppid.cache_activate(self)
|
||||||
|
# cached in case username() is used
|
||||||
|
if POSIX:
|
||||||
|
self.uids.cache_activate(self)
|
||||||
|
# specific implementation cache
|
||||||
|
self._proc.oneshot_enter()
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self.cpu_times.cache_deactivate(self)
|
||||||
|
self.memory_info.cache_deactivate(self)
|
||||||
|
self.ppid.cache_deactivate(self)
|
||||||
|
if POSIX:
|
||||||
|
self.uids.cache_deactivate(self)
|
||||||
|
self._proc.oneshot_exit()
|
||||||
|
|
||||||
def as_dict(self, attrs=None, ad_value=None):
|
def as_dict(self, attrs=None, ad_value=None):
|
||||||
"""Utility method returning process information as a
|
"""Utility method returning process information as a
|
||||||
|
@ -541,6 +653,9 @@ class Process(object):
|
||||||
checking whether PID has been reused.
|
checking whether PID has been reused.
|
||||||
If no parent is known return None.
|
If no parent is known return None.
|
||||||
"""
|
"""
|
||||||
|
lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0]
|
||||||
|
if self.pid == lowest_pid:
|
||||||
|
return None
|
||||||
ppid = self.ppid()
|
ppid = self.ppid()
|
||||||
if ppid is not None:
|
if ppid is not None:
|
||||||
ctime = self.create_time()
|
ctime = self.create_time()
|
||||||
|
@ -552,6 +667,17 @@ class Process(object):
|
||||||
except NoSuchProcess:
|
except NoSuchProcess:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def parents(self):
|
||||||
|
"""Return the parents of this process as a list of Process
|
||||||
|
instances. If no parents are known return an empty list.
|
||||||
|
"""
|
||||||
|
parents = []
|
||||||
|
proc = self.parent()
|
||||||
|
while proc is not None:
|
||||||
|
parents.append(proc)
|
||||||
|
proc = proc.parent()
|
||||||
|
return parents
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
"""Return whether this process is running.
|
"""Return whether this process is running.
|
||||||
It also checks if PID has been reused by another process in
|
It also checks if PID has been reused by another process in
|
||||||
|
@ -800,9 +926,6 @@ class Process(object):
|
||||||
(and set).
|
(and set).
|
||||||
(Windows, Linux and BSD only).
|
(Windows, Linux and BSD only).
|
||||||
"""
|
"""
|
||||||
# Automatically remove duplicates both on get and
|
|
||||||
# set (for get it's not really necessary, it's
|
|
||||||
# just for extra safety).
|
|
||||||
if cpus is None:
|
if cpus is None:
|
||||||
return list(set(self._proc.cpu_affinity_get()))
|
return list(set(self._proc.cpu_affinity_get()))
|
||||||
else:
|
else:
|
||||||
|
@ -826,7 +949,7 @@ class Process(object):
|
||||||
"""
|
"""
|
||||||
return self._proc.cpu_num()
|
return self._proc.cpu_num()
|
||||||
|
|
||||||
# Linux, macOS and Windows only
|
# Linux, macOS, Windows, Solaris, AIX
|
||||||
if hasattr(_psplatform.Process, "environ"):
|
if hasattr(_psplatform.Process, "environ"):
|
||||||
|
|
||||||
def environ(self):
|
def environ(self):
|
||||||
|
@ -1096,7 +1219,6 @@ class Process(object):
|
||||||
return (value / float(total_phymem)) * 100
|
return (value / float(total_phymem)) * 100
|
||||||
|
|
||||||
if hasattr(_psplatform.Process, "memory_maps"):
|
if hasattr(_psplatform.Process, "memory_maps"):
|
||||||
# Available everywhere except OpenBSD and NetBSD.
|
|
||||||
def memory_maps(self, grouped=True):
|
def memory_maps(self, grouped=True):
|
||||||
"""Return process' mapped memory regions as a list of namedtuples
|
"""Return process' mapped memory regions as a list of namedtuples
|
||||||
whose fields are variable depending on the platform.
|
whose fields are variable depending on the platform.
|
||||||
|
@ -1299,7 +1421,7 @@ class Popen(Process):
|
||||||
http://bugs.python.org/issue6973.
|
http://bugs.python.org/issue6973.
|
||||||
|
|
||||||
For a complete documentation refer to:
|
For a complete documentation refer to:
|
||||||
http://docs.python.org/library/subprocess.html
|
http://docs.python.org/3/library/subprocess.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1355,7 +1477,7 @@ class Popen(Process):
|
||||||
_as_dict_attrnames = set(
|
_as_dict_attrnames = set(
|
||||||
[x for x in dir(Process) if not x.startswith('_') and x not in
|
[x for x in dir(Process) if not x.startswith('_') and x not in
|
||||||
['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
|
['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
|
||||||
'is_running', 'as_dict', 'parent', 'children', 'rlimit',
|
'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit',
|
||||||
'memory_info_ex', 'oneshot']])
|
'memory_info_ex', 'oneshot']])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1366,7 +1488,10 @@ _as_dict_attrnames = set(
|
||||||
|
|
||||||
def pids():
|
def pids():
|
||||||
"""Return a list of current running PIDs."""
|
"""Return a list of current running PIDs."""
|
||||||
return _psplatform.pids()
|
global _LOWEST_PID
|
||||||
|
ret = sorted(_psplatform.pids())
|
||||||
|
_LOWEST_PID = ret[0]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def pid_exists(pid):
|
def pid_exists(pid):
|
||||||
|
@ -1388,6 +1513,7 @@ def pid_exists(pid):
|
||||||
|
|
||||||
|
|
||||||
_pmap = {}
|
_pmap = {}
|
||||||
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def process_iter(attrs=None, ad_value=None):
|
def process_iter(attrs=None, ad_value=None):
|
||||||
|
@ -1415,21 +1541,26 @@ def process_iter(attrs=None, ad_value=None):
|
||||||
proc = Process(pid)
|
proc = Process(pid)
|
||||||
if attrs is not None:
|
if attrs is not None:
|
||||||
proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value)
|
proc.info = proc.as_dict(attrs=attrs, ad_value=ad_value)
|
||||||
_pmap[proc.pid] = proc
|
with _lock:
|
||||||
|
_pmap[proc.pid] = proc
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
def remove(pid):
|
def remove(pid):
|
||||||
_pmap.pop(pid, None)
|
with _lock:
|
||||||
|
_pmap.pop(pid, None)
|
||||||
|
|
||||||
a = set(pids())
|
a = set(pids())
|
||||||
b = set(_pmap.keys())
|
b = set(_pmap.keys())
|
||||||
new_pids = a - b
|
new_pids = a - b
|
||||||
gone_pids = b - a
|
gone_pids = b - a
|
||||||
|
|
||||||
for pid in gone_pids:
|
for pid in gone_pids:
|
||||||
remove(pid)
|
remove(pid)
|
||||||
for pid, proc in sorted(list(_pmap.items()) +
|
|
||||||
list(dict.fromkeys(new_pids).items())):
|
with _lock:
|
||||||
|
ls = sorted(list(_pmap.items()) +
|
||||||
|
list(dict.fromkeys(new_pids).items()))
|
||||||
|
|
||||||
|
for pid, proc in ls:
|
||||||
try:
|
try:
|
||||||
if proc is None: # new process
|
if proc is None: # new process
|
||||||
yield add(pid)
|
yield add(pid)
|
||||||
|
@ -1609,14 +1740,12 @@ try:
|
||||||
except Exception:
|
except Exception:
|
||||||
# Don't want to crash at import time.
|
# Don't want to crash at import time.
|
||||||
_last_cpu_times = None
|
_last_cpu_times = None
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_last_per_cpu_times = cpu_times(percpu=True)
|
_last_per_cpu_times = cpu_times(percpu=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Don't want to crash at import time.
|
# Don't want to crash at import time.
|
||||||
_last_per_cpu_times = None
|
_last_per_cpu_times = None
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
|
|
||||||
def _cpu_tot_time(times):
|
def _cpu_tot_time(times):
|
||||||
|
@ -1864,18 +1993,41 @@ if hasattr(_psplatform, "cpu_freq"):
|
||||||
return ret[0]
|
return ret[0]
|
||||||
else:
|
else:
|
||||||
currs, mins, maxs = 0.0, 0.0, 0.0
|
currs, mins, maxs = 0.0, 0.0, 0.0
|
||||||
|
set_none = False
|
||||||
for cpu in ret:
|
for cpu in ret:
|
||||||
currs += cpu.current
|
currs += cpu.current
|
||||||
|
# On Linux if /proc/cpuinfo is used min/max are set
|
||||||
|
# to None.
|
||||||
|
if LINUX and cpu.min is None:
|
||||||
|
set_none = True
|
||||||
|
continue
|
||||||
mins += cpu.min
|
mins += cpu.min
|
||||||
maxs += cpu.max
|
maxs += cpu.max
|
||||||
|
|
||||||
current = currs / num_cpus
|
current = currs / num_cpus
|
||||||
min_ = mins / num_cpus
|
|
||||||
max_ = maxs / num_cpus
|
if set_none:
|
||||||
|
min_ = max_ = None
|
||||||
|
else:
|
||||||
|
min_ = mins / num_cpus
|
||||||
|
max_ = maxs / num_cpus
|
||||||
|
|
||||||
return _common.scpufreq(current, min_, max_)
|
return _common.scpufreq(current, min_, max_)
|
||||||
|
|
||||||
__all__.append("cpu_freq")
|
__all__.append("cpu_freq")
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"):
|
||||||
|
# Perform this hasattr check once on import time to either use the
|
||||||
|
# platform based code or proxy straight from the os module.
|
||||||
|
if hasattr(os, "getloadavg"):
|
||||||
|
getloadavg = os.getloadavg
|
||||||
|
else:
|
||||||
|
getloadavg = _psplatform.getloadavg
|
||||||
|
|
||||||
|
__all__.append("getloadavg")
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- system memory related functions
|
# --- system memory related functions
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -1901,7 +2053,7 @@ def virtual_memory():
|
||||||
- used:
|
- used:
|
||||||
memory used, calculated differently depending on the platform and
|
memory used, calculated differently depending on the platform and
|
||||||
designed for informational purposes only:
|
designed for informational purposes only:
|
||||||
macOS: active + inactive + wired
|
macOS: active + wired
|
||||||
BSD: active + wired + cached
|
BSD: active + wired + cached
|
||||||
Linux: total - free
|
Linux: total - free
|
||||||
|
|
||||||
|
@ -2297,19 +2449,16 @@ if WINDOWS:
|
||||||
|
|
||||||
|
|
||||||
def test(): # pragma: no cover
|
def test(): # pragma: no cover
|
||||||
"""List info of all currently running processes emulating ps aux
|
from ._common import bytes2human
|
||||||
output.
|
from ._compat import get_terminal_size
|
||||||
"""
|
|
||||||
today_day = datetime.date.today()
|
today_day = datetime.date.today()
|
||||||
templ = "%-10s %5s %4s %7s %7s %-13s %5s %7s %s"
|
templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s %s"
|
||||||
attrs = ['pid', 'memory_percent', 'name', 'cpu_times', 'create_time',
|
attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times',
|
||||||
'memory_info']
|
'create_time', 'memory_info', 'status', 'nice', 'username']
|
||||||
if POSIX:
|
print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE",
|
||||||
attrs.append('uids')
|
"STATUS", "START", "TIME", "CMDLINE"))
|
||||||
attrs.append('terminal')
|
for p in process_iter(attrs, ad_value=None):
|
||||||
print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "TTY", "START", "TIME",
|
|
||||||
"COMMAND"))
|
|
||||||
for p in process_iter(attrs=attrs, ad_value=''):
|
|
||||||
if p.info['create_time']:
|
if p.info['create_time']:
|
||||||
ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
|
ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
|
||||||
if ctime.date() == today_day:
|
if ctime.date() == today_day:
|
||||||
|
@ -2318,30 +2467,46 @@ def test(): # pragma: no cover
|
||||||
ctime = ctime.strftime("%b%d")
|
ctime = ctime.strftime("%b%d")
|
||||||
else:
|
else:
|
||||||
ctime = ''
|
ctime = ''
|
||||||
cputime = time.strftime("%M:%S",
|
if p.info['cpu_times']:
|
||||||
time.localtime(sum(p.info['cpu_times'])))
|
cputime = time.strftime("%M:%S",
|
||||||
try:
|
time.localtime(sum(p.info['cpu_times'])))
|
||||||
user = p.username()
|
else:
|
||||||
except Error:
|
cputime = ''
|
||||||
user = ''
|
|
||||||
if WINDOWS and '\\' in user:
|
user = p.info['username'] or ''
|
||||||
|
if not user and POSIX:
|
||||||
|
try:
|
||||||
|
user = p.uids()[0]
|
||||||
|
except Error:
|
||||||
|
pass
|
||||||
|
if user and WINDOWS and '\\' in user:
|
||||||
user = user.split('\\')[1]
|
user = user.split('\\')[1]
|
||||||
vms = p.info['memory_info'] and \
|
user = user[:9]
|
||||||
int(p.info['memory_info'].vms / 1024) or '?'
|
vms = bytes2human(p.info['memory_info'].vms) if \
|
||||||
rss = p.info['memory_info'] and \
|
p.info['memory_info'] is not None else ''
|
||||||
int(p.info['memory_info'].rss / 1024) or '?'
|
rss = bytes2human(p.info['memory_info'].rss) if \
|
||||||
memp = p.info['memory_percent'] and \
|
p.info['memory_info'] is not None else ''
|
||||||
round(p.info['memory_percent'], 1) or '?'
|
memp = round(p.info['memory_percent'], 1) if \
|
||||||
print(templ % (
|
p.info['memory_percent'] is not None else ''
|
||||||
|
nice = int(p.info['nice']) if p.info['nice'] else ''
|
||||||
|
if p.info['cmdline']:
|
||||||
|
cmdline = ' '.join(p.info['cmdline'])
|
||||||
|
else:
|
||||||
|
cmdline = p.info['name']
|
||||||
|
status = p.info['status'][:5] if p.info['status'] else ''
|
||||||
|
|
||||||
|
line = templ % (
|
||||||
user[:10],
|
user[:10],
|
||||||
p.info['pid'],
|
p.info['pid'],
|
||||||
memp,
|
memp,
|
||||||
vms,
|
vms,
|
||||||
rss,
|
rss,
|
||||||
p.info.get('terminal', '') or '?',
|
nice,
|
||||||
|
status,
|
||||||
ctime,
|
ctime,
|
||||||
cputime,
|
cputime,
|
||||||
p.info['name'].strip() or '?'))
|
cmdline)
|
||||||
|
print(line[:get_terminal_size()[0]])
|
||||||
|
|
||||||
|
|
||||||
del memoize, memoize_when_activated, division, deprecated_method
|
del memoize, memoize_when_activated, division, deprecated_method
|
||||||
|
|
|
@ -64,6 +64,7 @@ __all__ = [
|
||||||
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
|
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
|
||||||
'parse_environ_block', 'path_exists_strict', 'usage_percent',
|
'parse_environ_block', 'path_exists_strict', 'usage_percent',
|
||||||
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
|
'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
|
||||||
|
'bytes2human',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,7 +328,7 @@ def memoize_when_activated(fun):
|
||||||
1
|
1
|
||||||
>>>
|
>>>
|
||||||
>>> # activated
|
>>> # activated
|
||||||
>>> foo.cache_activate()
|
>>> foo.cache_activate(self)
|
||||||
>>> foo()
|
>>> foo()
|
||||||
1
|
1
|
||||||
>>> foo()
|
>>> foo()
|
||||||
|
@ -336,26 +337,30 @@ def memoize_when_activated(fun):
|
||||||
"""
|
"""
|
||||||
@functools.wraps(fun)
|
@functools.wraps(fun)
|
||||||
def wrapper(self):
|
def wrapper(self):
|
||||||
if not wrapper.cache_activated:
|
try:
|
||||||
|
# case 1: we previously entered oneshot() ctx
|
||||||
|
ret = self._cache[fun]
|
||||||
|
except AttributeError:
|
||||||
|
# case 2: we never entered oneshot() ctx
|
||||||
return fun(self)
|
return fun(self)
|
||||||
else:
|
except KeyError:
|
||||||
try:
|
# case 3: we entered oneshot() ctx but there's no cache
|
||||||
ret = cache[fun]
|
# for this entry yet
|
||||||
except KeyError:
|
ret = self._cache[fun] = fun(self)
|
||||||
ret = cache[fun] = fun(self)
|
return ret
|
||||||
return ret
|
|
||||||
|
|
||||||
def cache_activate():
|
def cache_activate(proc):
|
||||||
"""Activate cache."""
|
"""Activate cache. Expects a Process instance. Cache will be
|
||||||
wrapper.cache_activated = True
|
stored as a "_cache" instance attribute."""
|
||||||
|
proc._cache = {}
|
||||||
|
|
||||||
def cache_deactivate():
|
def cache_deactivate(proc):
|
||||||
"""Deactivate and clear cache."""
|
"""Deactivate and clear cache."""
|
||||||
wrapper.cache_activated = False
|
try:
|
||||||
cache.clear()
|
del proc._cache
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
cache = {}
|
|
||||||
wrapper.cache_activated = False
|
|
||||||
wrapper.cache_activate = cache_activate
|
wrapper.cache_activate = cache_activate
|
||||||
wrapper.cache_deactivate = cache_deactivate
|
wrapper.cache_deactivate = cache_deactivate
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -471,7 +476,7 @@ def deprecated_method(replacement):
|
||||||
|
|
||||||
@functools.wraps(fun)
|
@functools.wraps(fun)
|
||||||
def inner(self, *args, **kwargs):
|
def inner(self, *args, **kwargs):
|
||||||
warnings.warn(msg, category=FutureWarning, stacklevel=2)
|
warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
|
||||||
return getattr(self, replacement)(*args, **kwargs)
|
return getattr(self, replacement)(*args, **kwargs)
|
||||||
return inner
|
return inner
|
||||||
return outer
|
return outer
|
||||||
|
@ -576,3 +581,54 @@ def wrap_numbers(input_dict, name):
|
||||||
_wn = _WrapNumbers()
|
_wn = _WrapNumbers()
|
||||||
wrap_numbers.cache_clear = _wn.cache_clear
|
wrap_numbers.cache_clear = _wn.cache_clear
|
||||||
wrap_numbers.cache_info = _wn.cache_info
|
wrap_numbers.cache_info = _wn.cache_info
|
||||||
|
|
||||||
|
|
||||||
|
def open_binary(fname, **kwargs):
|
||||||
|
return open(fname, "rb", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def open_text(fname, **kwargs):
|
||||||
|
"""On Python 3 opens a file in text mode by using fs encoding and
|
||||||
|
a proper en/decoding errors handler.
|
||||||
|
On Python 2 this is just an alias for open(name, 'rt').
|
||||||
|
"""
|
||||||
|
if PY3:
|
||||||
|
# See:
|
||||||
|
# https://github.com/giampaolo/psutil/issues/675
|
||||||
|
# https://github.com/giampaolo/psutil/pull/733
|
||||||
|
kwargs.setdefault('encoding', ENCODING)
|
||||||
|
kwargs.setdefault('errors', ENCODING_ERRS)
|
||||||
|
return open(fname, "rt", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def bytes2human(n, format="%(value).1f%(symbol)s"):
|
||||||
|
"""Used by various scripts. See:
|
||||||
|
http://goo.gl/zeJZl
|
||||||
|
|
||||||
|
>>> bytes2human(10000)
|
||||||
|
'9.8K'
|
||||||
|
>>> bytes2human(100001221)
|
||||||
|
'95.4M'
|
||||||
|
"""
|
||||||
|
symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||||
|
prefix = {}
|
||||||
|
for i, s in enumerate(symbols[1:]):
|
||||||
|
prefix[s] = 1 << (i + 1) * 10
|
||||||
|
for symbol in reversed(symbols[1:]):
|
||||||
|
if n >= prefix[symbol]:
|
||||||
|
value = float(n) / prefix[symbol]
|
||||||
|
return format % locals()
|
||||||
|
return format % dict(symbol=symbols[0], value=n)
|
||||||
|
|
||||||
|
|
||||||
|
def get_procfs_path():
|
||||||
|
"""Return updated psutil.PROCFS_PATH constant."""
|
||||||
|
return sys.modules['psutil'].PROCFS_PATH
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def decode(s):
|
||||||
|
return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
|
||||||
|
else:
|
||||||
|
def decode(s):
|
||||||
|
return s
|
||||||
|
|
|
@ -10,7 +10,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
|
__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
|
||||||
"lru_cache", "which"]
|
"lru_cache", "which", "get_terminal_size"]
|
||||||
|
|
||||||
PY3 = sys.version_info[0] == 3
|
PY3 = sys.version_info[0] == 3
|
||||||
|
|
||||||
|
@ -239,3 +239,24 @@ except ImportError:
|
||||||
if _access_check(name, mode):
|
if _access_check(name, mode):
|
||||||
return name
|
return name
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# python 3.3
|
||||||
|
try:
|
||||||
|
from shutil import get_terminal_size
|
||||||
|
except ImportError:
|
||||||
|
def get_terminal_size(fallback=(80, 24)):
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
import termios
|
||||||
|
import struct
|
||||||
|
except ImportError:
|
||||||
|
return fallback
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# This should work on Linux.
|
||||||
|
res = struct.unpack(
|
||||||
|
'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234'))
|
||||||
|
return (res[1], res[0])
|
||||||
|
except Exception:
|
||||||
|
return fallback
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
|
||||||
"""Base exception class. All other psutil exceptions inherit
|
|
||||||
from this one.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, msg=""):
|
|
||||||
Exception.__init__(self, msg)
|
|
||||||
self.msg = msg
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
|
|
||||||
return ret.strip()
|
|
||||||
|
|
||||||
__str__ = __repr__
|
|
||||||
|
|
||||||
|
|
||||||
class NoSuchProcess(Error):
|
|
||||||
"""Exception raised when a process with a certain PID doesn't
|
|
||||||
or no longer exists.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, pid, name=None, msg=None):
|
|
||||||
Error.__init__(self, msg)
|
|
||||||
self.pid = pid
|
|
||||||
self.name = name
|
|
||||||
self.msg = msg
|
|
||||||
if msg is None:
|
|
||||||
if name:
|
|
||||||
details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
|
|
||||||
else:
|
|
||||||
details = "(pid=%s)" % self.pid
|
|
||||||
self.msg = "process no longer exists " + details
|
|
||||||
|
|
||||||
|
|
||||||
class ZombieProcess(NoSuchProcess):
|
|
||||||
"""Exception raised when querying a zombie process. This is
|
|
||||||
raised on macOS, BSD and Solaris only, and not always: depending
|
|
||||||
on the query the OS may be able to succeed anyway.
|
|
||||||
On Linux all zombie processes are querable (hence this is never
|
|
||||||
raised). Windows doesn't have zombie processes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, pid, name=None, ppid=None, msg=None):
|
|
||||||
NoSuchProcess.__init__(self, msg)
|
|
||||||
self.pid = pid
|
|
||||||
self.ppid = ppid
|
|
||||||
self.name = name
|
|
||||||
self.msg = msg
|
|
||||||
if msg is None:
|
|
||||||
args = ["pid=%s" % pid]
|
|
||||||
if name:
|
|
||||||
args.append("name=%s" % repr(self.name))
|
|
||||||
if ppid:
|
|
||||||
args.append("ppid=%s" % self.ppid)
|
|
||||||
details = "(%s)" % ", ".join(args)
|
|
||||||
self.msg = "process still exists but it's a zombie " + details
|
|
||||||
|
|
||||||
|
|
||||||
class AccessDenied(Error):
|
|
||||||
"""Exception raised when permission to perform an action is denied."""
|
|
||||||
|
|
||||||
def __init__(self, pid=None, name=None, msg=None):
|
|
||||||
Error.__init__(self, msg)
|
|
||||||
self.pid = pid
|
|
||||||
self.name = name
|
|
||||||
self.msg = msg
|
|
||||||
if msg is None:
|
|
||||||
if (pid is not None) and (name is not None):
|
|
||||||
self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
|
|
||||||
elif (pid is not None):
|
|
||||||
self.msg = "(pid=%s)" % self.pid
|
|
||||||
else:
|
|
||||||
self.msg = ""
|
|
||||||
|
|
||||||
|
|
||||||
class TimeoutExpired(Error):
|
|
||||||
"""Raised on Process.wait(timeout) if timeout expires and process
|
|
||||||
is still alive.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, seconds, pid=None, name=None):
|
|
||||||
Error.__init__(self, "timeout after %s seconds" % seconds)
|
|
||||||
self.seconds = seconds
|
|
||||||
self.pid = pid
|
|
||||||
self.name = name
|
|
||||||
if (pid is not None) and (name is not None):
|
|
||||||
self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
|
|
||||||
elif (pid is not None):
|
|
||||||
self.msg += " (pid=%s)" % self.pid
|
|
|
@ -7,6 +7,7 @@
|
||||||
"""AIX platform implementation."""
|
"""AIX platform implementation."""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import functools
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -20,6 +21,7 @@ from . import _psposix
|
||||||
from . import _psutil_aix as cext
|
from . import _psutil_aix as cext
|
||||||
from . import _psutil_posix as cext_posix
|
from . import _psutil_posix as cext_posix
|
||||||
from ._common import AF_INET6
|
from ._common import AF_INET6
|
||||||
|
from ._common import get_procfs_path
|
||||||
from ._common import memoize_when_activated
|
from ._common import memoize_when_activated
|
||||||
from ._common import NIC_DUPLEX_FULL
|
from ._common import NIC_DUPLEX_FULL
|
||||||
from ._common import NIC_DUPLEX_HALF
|
from ._common import NIC_DUPLEX_HALF
|
||||||
|
@ -28,9 +30,6 @@ from ._common import sockfam_to_enum
|
||||||
from ._common import socktype_to_enum
|
from ._common import socktype_to_enum
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._compat import PY3
|
from ._compat import PY3
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
|
|
||||||
__extra__all__ = ["PROCFS_PATH"]
|
__extra__all__ = ["PROCFS_PATH"]
|
||||||
|
@ -42,6 +41,8 @@ __extra__all__ = ["PROCFS_PATH"]
|
||||||
|
|
||||||
|
|
||||||
HAS_THREADS = hasattr(cext, "proc_threads")
|
HAS_THREADS = hasattr(cext, "proc_threads")
|
||||||
|
HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters")
|
||||||
|
HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters")
|
||||||
|
|
||||||
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
|
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
|
||||||
AF_LINK = cext_posix.AF_LINK
|
AF_LINK = cext_posix.AF_LINK
|
||||||
|
@ -79,6 +80,13 @@ proc_info_map = dict(
|
||||||
status=6,
|
status=6,
|
||||||
ttynr=7)
|
ttynr=7)
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -93,21 +101,6 @@ pfullmem = pmem
|
||||||
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
|
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
|
||||||
# psutil.virtual_memory()
|
# psutil.virtual_memory()
|
||||||
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
|
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
|
||||||
# psutil.Process.memory_maps(grouped=True)
|
|
||||||
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
|
|
||||||
# psutil.Process.memory_maps(grouped=False)
|
|
||||||
pmmap_ext = namedtuple(
|
|
||||||
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- utils
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def get_procfs_path():
|
|
||||||
"""Return updated psutil.PROCFS_PATH constant."""
|
|
||||||
return sys.modules['psutil'].PROCFS_PATH
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -212,7 +205,9 @@ def disk_partitions(all=False):
|
||||||
|
|
||||||
|
|
||||||
net_if_addrs = cext_posix.net_if_addrs
|
net_if_addrs = cext_posix.net_if_addrs
|
||||||
net_io_counters = cext.net_io_counters
|
|
||||||
|
if HAS_NET_IO_COUNTERS:
|
||||||
|
net_io_counters = cext.net_io_counters
|
||||||
|
|
||||||
|
|
||||||
def net_connections(kind, _pid=-1):
|
def net_connections(kind, _pid=-1):
|
||||||
|
@ -328,7 +323,7 @@ def wrap_exceptions(fun):
|
||||||
"""Call callable into a try/except clause and translate ENOENT,
|
"""Call callable into a try/except clause and translate ENOENT,
|
||||||
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
|
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
|
||||||
"""
|
"""
|
||||||
|
@functools.wraps(fun)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return fun(self, *args, **kwargs)
|
return fun(self, *args, **kwargs)
|
||||||
|
@ -354,7 +349,7 @@ def wrap_exceptions(fun):
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Wrapper class around underlying C implementation."""
|
"""Wrapper class around underlying C implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
|
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
|
@ -363,23 +358,19 @@ class Process(object):
|
||||||
self._procfs_path = get_procfs_path()
|
self._procfs_path = get_procfs_path()
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self._proc_name_and_args.cache_activate()
|
self._proc_basic_info.cache_activate(self)
|
||||||
self._proc_basic_info.cache_activate()
|
self._proc_cred.cache_activate(self)
|
||||||
self._proc_cred.cache_activate()
|
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self._proc_name_and_args.cache_deactivate()
|
self._proc_basic_info.cache_deactivate(self)
|
||||||
self._proc_basic_info.cache_deactivate()
|
self._proc_cred.cache_deactivate(self)
|
||||||
self._proc_cred.cache_deactivate()
|
|
||||||
|
|
||||||
@memoize_when_activated
|
|
||||||
def _proc_name_and_args(self):
|
|
||||||
return cext.proc_name_and_args(self.pid, self._procfs_path)
|
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _proc_basic_info(self):
|
def _proc_basic_info(self):
|
||||||
return cext.proc_basic_info(self.pid, self._procfs_path)
|
return cext.proc_basic_info(self.pid, self._procfs_path)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _proc_cred(self):
|
def _proc_cred(self):
|
||||||
return cext.proc_cred(self.pid, self._procfs_path)
|
return cext.proc_cred(self.pid, self._procfs_path)
|
||||||
|
@ -388,22 +379,25 @@ class Process(object):
|
||||||
def name(self):
|
def name(self):
|
||||||
if self.pid == 0:
|
if self.pid == 0:
|
||||||
return "swapper"
|
return "swapper"
|
||||||
# note: this is limited to 15 characters
|
# note: max 16 characters
|
||||||
return self._proc_name_and_args()[0].rstrip("\x00")
|
return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00")
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def exe(self):
|
def exe(self):
|
||||||
# there is no way to get executable path in AIX other than to guess,
|
# there is no way to get executable path in AIX other than to guess,
|
||||||
# and guessing is more complex than what's in the wrapping class
|
# and guessing is more complex than what's in the wrapping class
|
||||||
exe = self.cmdline()[0]
|
cmdline = self.cmdline()
|
||||||
|
if not cmdline:
|
||||||
|
return ''
|
||||||
|
exe = cmdline[0]
|
||||||
if os.path.sep in exe:
|
if os.path.sep in exe:
|
||||||
# relative or absolute path
|
# relative or absolute path
|
||||||
if not os.path.isabs(exe):
|
if not os.path.isabs(exe):
|
||||||
# if cwd has changed, we're out of luck - this may be wrong!
|
# if cwd has changed, we're out of luck - this may be wrong!
|
||||||
exe = os.path.abspath(os.path.join(self.cwd(), exe))
|
exe = os.path.abspath(os.path.join(self.cwd(), exe))
|
||||||
if (os.path.isabs(exe) and
|
if (os.path.isabs(exe) and
|
||||||
os.path.isfile(exe) and
|
os.path.isfile(exe) and
|
||||||
os.access(exe, os.X_OK)):
|
os.access(exe, os.X_OK)):
|
||||||
return exe
|
return exe
|
||||||
# not found, move to search in PATH using basename only
|
# not found, move to search in PATH using basename only
|
||||||
exe = os.path.basename(exe)
|
exe = os.path.basename(exe)
|
||||||
|
@ -411,13 +405,17 @@ class Process(object):
|
||||||
for path in os.environ["PATH"].split(":"):
|
for path in os.environ["PATH"].split(":"):
|
||||||
possible_exe = os.path.abspath(os.path.join(path, exe))
|
possible_exe = os.path.abspath(os.path.join(path, exe))
|
||||||
if (os.path.isfile(possible_exe) and
|
if (os.path.isfile(possible_exe) and
|
||||||
os.access(possible_exe, os.X_OK)):
|
os.access(possible_exe, os.X_OK)):
|
||||||
return possible_exe
|
return possible_exe
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cmdline(self):
|
def cmdline(self):
|
||||||
return self._proc_name_and_args()[1].split(' ')
|
return cext.proc_args(self.pid)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
|
def environ(self):
|
||||||
|
return cext.proc_environ(self.pid)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def create_time(self):
|
def create_time(self):
|
||||||
|
@ -561,14 +559,15 @@ class Process(object):
|
||||||
def wait(self, timeout=None):
|
def wait(self, timeout=None):
|
||||||
return _psposix.wait_pid(self.pid, timeout, self._name)
|
return _psposix.wait_pid(self.pid, timeout, self._name)
|
||||||
|
|
||||||
@wrap_exceptions
|
if HAS_PROC_IO_COUNTERS:
|
||||||
def io_counters(self):
|
@wrap_exceptions
|
||||||
try:
|
def io_counters(self):
|
||||||
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
|
try:
|
||||||
except OSError:
|
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
|
||||||
# if process is terminated, proc_io_counters returns OSError
|
except OSError:
|
||||||
# instead of NSP
|
# if process is terminated, proc_io_counters returns OSError
|
||||||
if not pid_exists(self.pid):
|
# instead of NSP
|
||||||
raise NoSuchProcess(self.pid, self._name)
|
if not pid_exists(self.pid):
|
||||||
raise
|
raise NoSuchProcess(self.pid, self._name)
|
||||||
return _common.pio(rc, wc, rb, wb)
|
raise
|
||||||
|
return _common.pio(rc, wc, rb, wb)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import os
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from socket import AF_INET
|
from socket import AF_INET
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from . import _common
|
from . import _common
|
||||||
from . import _psposix
|
from . import _psposix
|
||||||
|
@ -27,9 +28,6 @@ from ._common import sockfam_to_enum
|
||||||
from ._common import socktype_to_enum
|
from ._common import socktype_to_enum
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._compat import which
|
from ._compat import which
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
__extra__all__ = []
|
__extra__all__ = []
|
||||||
|
|
||||||
|
@ -103,6 +101,11 @@ else:
|
||||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
||||||
AF_LINK = cext_posix.AF_LINK
|
AF_LINK = cext_posix.AF_LINK
|
||||||
|
|
||||||
|
HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
|
||||||
|
HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads")
|
||||||
|
HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files')
|
||||||
|
HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds')
|
||||||
|
|
||||||
kinfo_proc_map = dict(
|
kinfo_proc_map = dict(
|
||||||
ppid=0,
|
ppid=0,
|
||||||
status=1,
|
status=1,
|
||||||
|
@ -131,6 +134,13 @@ kinfo_proc_map = dict(
|
||||||
name=24,
|
name=24,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -211,7 +221,7 @@ def cpu_times():
|
||||||
return scputimes(user, nice, system, idle, irq)
|
return scputimes(user, nice, system, idle, irq)
|
||||||
|
|
||||||
|
|
||||||
if hasattr(cext, "per_cpu_times"):
|
if HAS_PER_CPU_TIMES:
|
||||||
def per_cpu_times():
|
def per_cpu_times():
|
||||||
"""Return system CPU times as a namedtuple"""
|
"""Return system CPU times as a namedtuple"""
|
||||||
ret = []
|
ret = []
|
||||||
|
@ -432,6 +442,47 @@ if FREEBSD:
|
||||||
secsleft = minsleft * 60
|
secsleft = minsleft * 60
|
||||||
return _common.sbattery(percent, secsleft, power_plugged)
|
return _common.sbattery(percent, secsleft, power_plugged)
|
||||||
|
|
||||||
|
def sensors_temperatures():
|
||||||
|
"Return CPU cores temperatures if available, else an empty dict."
|
||||||
|
ret = defaultdict(list)
|
||||||
|
num_cpus = cpu_count_logical()
|
||||||
|
for cpu in range(num_cpus):
|
||||||
|
try:
|
||||||
|
current, high = cext.sensors_cpu_temperature(cpu)
|
||||||
|
if high <= 0:
|
||||||
|
high = None
|
||||||
|
name = "Core %s" % cpu
|
||||||
|
ret["coretemp"].append(
|
||||||
|
_common.shwtemp(name, current, high, high))
|
||||||
|
except NotImplementedError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def cpu_freq():
|
||||||
|
"""Return frequency metrics for CPUs. As of Dec 2018 only
|
||||||
|
CPU 0 appears to be supported by FreeBSD and all other cores
|
||||||
|
match the frequency of CPU 0.
|
||||||
|
"""
|
||||||
|
ret = []
|
||||||
|
num_cpus = cpu_count_logical()
|
||||||
|
for cpu in range(num_cpus):
|
||||||
|
try:
|
||||||
|
current, available_freq = cext.cpu_frequency(cpu)
|
||||||
|
except NotImplementedError:
|
||||||
|
continue
|
||||||
|
if available_freq:
|
||||||
|
try:
|
||||||
|
min_freq = int(available_freq.split(" ")[-1].split("/")[0])
|
||||||
|
except(IndexError, ValueError):
|
||||||
|
min_freq = None
|
||||||
|
try:
|
||||||
|
max_freq = int(available_freq.split(" ")[0].split("/")[0])
|
||||||
|
except(IndexError, ValueError):
|
||||||
|
max_freq = None
|
||||||
|
ret.append(_common.scpufreq(current, min_freq, max_freq))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- other system functions
|
# --- other system functions
|
||||||
|
@ -547,13 +598,20 @@ def wrap_exceptions_procfs(inst):
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Wrapper class around underlying C implementation."""
|
"""Wrapper class around underlying C implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid"]
|
__slots__ = ["pid", "_name", "_ppid", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self._name = None
|
self._name = None
|
||||||
self._ppid = None
|
self._ppid = None
|
||||||
|
|
||||||
|
def _assert_alive(self):
|
||||||
|
"""Raise NSP if the process disappeared on us."""
|
||||||
|
# For those C function who do not raise NSP, possibly returning
|
||||||
|
# incorrect or incomplete result.
|
||||||
|
cext.proc_name(self.pid)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def oneshot(self):
|
def oneshot(self):
|
||||||
"""Retrieves multiple process info in one shot as a raw tuple."""
|
"""Retrieves multiple process info in one shot as a raw tuple."""
|
||||||
|
@ -562,10 +620,10 @@ class Process(object):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self.oneshot.cache_activate()
|
self.oneshot.cache_activate(self)
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self.oneshot.cache_deactivate()
|
self.oneshot.cache_deactivate(self)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -678,7 +736,7 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def num_threads(self):
|
def num_threads(self):
|
||||||
if hasattr(cext, "proc_num_threads"):
|
if HAS_PROC_NUM_THREADS:
|
||||||
# FreeBSD
|
# FreeBSD
|
||||||
return cext.proc_num_threads(self.pid)
|
return cext.proc_num_threads(self.pid)
|
||||||
else:
|
else:
|
||||||
|
@ -700,10 +758,7 @@ class Process(object):
|
||||||
ntuple = _common.pthread(thread_id, utime, stime)
|
ntuple = _common.pthread(thread_id, utime, stime)
|
||||||
retlist.append(ntuple)
|
retlist.append(ntuple)
|
||||||
if OPENBSD:
|
if OPENBSD:
|
||||||
# On OpenBSD the underlying C function does not raise NSP
|
self._assert_alive()
|
||||||
# in case the process is gone (and the returned list may
|
|
||||||
# incomplete).
|
|
||||||
self.name() # raise NSP if the process disappeared on us
|
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -733,10 +788,7 @@ class Process(object):
|
||||||
type = socktype_to_enum(type)
|
type = socktype_to_enum(type)
|
||||||
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
|
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
|
||||||
ret.add(nt)
|
ret.add(nt)
|
||||||
# On NetBSD the underlying C function does not raise NSP
|
self._assert_alive()
|
||||||
# in case the process is gone (and the returned list may
|
|
||||||
# incomplete).
|
|
||||||
self.name() # raise NSP if the process disappeared on us
|
|
||||||
return list(ret)
|
return list(ret)
|
||||||
|
|
||||||
families, types = conn_tmap[kind]
|
families, types = conn_tmap[kind]
|
||||||
|
@ -755,10 +807,7 @@ class Process(object):
|
||||||
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
|
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
|
||||||
ret.append(nt)
|
ret.append(nt)
|
||||||
if OPENBSD:
|
if OPENBSD:
|
||||||
# On OpenBSD the underlying C function does not raise NSP
|
self._assert_alive()
|
||||||
# in case the process is gone (and the returned list may
|
|
||||||
# incomplete).
|
|
||||||
self.name() # raise NSP if the process disappeared on us
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -798,7 +847,7 @@ class Process(object):
|
||||||
elif NETBSD:
|
elif NETBSD:
|
||||||
with wrap_exceptions_procfs(self):
|
with wrap_exceptions_procfs(self):
|
||||||
return os.readlink("/proc/%s/cwd" % self.pid)
|
return os.readlink("/proc/%s/cwd" % self.pid)
|
||||||
elif hasattr(cext, 'proc_open_files'):
|
elif HAS_PROC_OPEN_FILES:
|
||||||
# FreeBSD < 8 does not support functions based on
|
# FreeBSD < 8 does not support functions based on
|
||||||
# kinfo_getfile() and kinfo_getvmmap()
|
# kinfo_getfile() and kinfo_getvmmap()
|
||||||
return cext.proc_cwd(self.pid) or None
|
return cext.proc_cwd(self.pid) or None
|
||||||
|
@ -817,7 +866,7 @@ class Process(object):
|
||||||
|
|
||||||
# FreeBSD < 8 does not support functions based on kinfo_getfile()
|
# FreeBSD < 8 does not support functions based on kinfo_getfile()
|
||||||
# and kinfo_getvmmap()
|
# and kinfo_getvmmap()
|
||||||
if hasattr(cext, 'proc_open_files'):
|
if HAS_PROC_OPEN_FILES:
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def open_files(self):
|
def open_files(self):
|
||||||
"""Return files opened by process as a list of namedtuples."""
|
"""Return files opened by process as a list of namedtuples."""
|
||||||
|
@ -828,15 +877,13 @@ class Process(object):
|
||||||
|
|
||||||
# FreeBSD < 8 does not support functions based on kinfo_getfile()
|
# FreeBSD < 8 does not support functions based on kinfo_getfile()
|
||||||
# and kinfo_getvmmap()
|
# and kinfo_getvmmap()
|
||||||
if hasattr(cext, 'proc_num_fds'):
|
if HAS_PROC_NUM_FDS:
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def num_fds(self):
|
def num_fds(self):
|
||||||
"""Return the number of file descriptors opened by this process."""
|
"""Return the number of file descriptors opened by this process."""
|
||||||
ret = cext.proc_num_fds(self.pid)
|
ret = cext.proc_num_fds(self.pid)
|
||||||
if NETBSD:
|
if NETBSD:
|
||||||
# On NetBSD the underlying C function does not raise NSP
|
self._assert_alive()
|
||||||
# in case the process is gone.
|
|
||||||
self.name() # raise NSP if the process disappeared on us
|
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
num_fds = _not_implemented
|
num_fds = _not_implemented
|
||||||
|
|
|
@ -25,25 +25,23 @@ from . import _common
|
||||||
from . import _psposix
|
from . import _psposix
|
||||||
from . import _psutil_linux as cext
|
from . import _psutil_linux as cext
|
||||||
from . import _psutil_posix as cext_posix
|
from . import _psutil_posix as cext_posix
|
||||||
from ._common import ENCODING
|
from ._common import decode
|
||||||
from ._common import ENCODING_ERRS
|
from ._common import get_procfs_path
|
||||||
from ._common import isfile_strict
|
from ._common import isfile_strict
|
||||||
from ._common import memoize
|
from ._common import memoize
|
||||||
from ._common import memoize_when_activated
|
from ._common import memoize_when_activated
|
||||||
from ._common import NIC_DUPLEX_FULL
|
from ._common import NIC_DUPLEX_FULL
|
||||||
from ._common import NIC_DUPLEX_HALF
|
from ._common import NIC_DUPLEX_HALF
|
||||||
from ._common import NIC_DUPLEX_UNKNOWN
|
from ._common import NIC_DUPLEX_UNKNOWN
|
||||||
|
from ._common import open_binary
|
||||||
|
from ._common import open_text
|
||||||
from ._common import parse_environ_block
|
from ._common import parse_environ_block
|
||||||
from ._common import path_exists_strict
|
from ._common import path_exists_strict
|
||||||
from ._common import supports_ipv6
|
from ._common import supports_ipv6
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._compat import b
|
from ._compat import b
|
||||||
from ._compat import basestring
|
from ._compat import basestring
|
||||||
from ._compat import long
|
|
||||||
from ._compat import PY3
|
from ._compat import PY3
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 4):
|
if sys.version_info >= (3, 4):
|
||||||
import enum
|
import enum
|
||||||
|
@ -71,6 +69,7 @@ __extra__all__ = [
|
||||||
POWER_SUPPLY_PATH = "/sys/class/power_supply"
|
POWER_SUPPLY_PATH = "/sys/class/power_supply"
|
||||||
HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
|
HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
|
||||||
HAS_PRLIMIT = hasattr(cext, "linux_prlimit")
|
HAS_PRLIMIT = hasattr(cext, "linux_prlimit")
|
||||||
|
HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_ioprio_get")
|
||||||
_DEFAULT = object()
|
_DEFAULT = object()
|
||||||
|
|
||||||
# RLIMIT_* constants, not guaranteed to be present on all kernels
|
# RLIMIT_* constants, not guaranteed to be present on all kernels
|
||||||
|
@ -158,6 +157,13 @@ TCP_STATUSES = {
|
||||||
"0B": _common.CONN_CLOSING
|
"0B": _common.CONN_CLOSING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -201,37 +207,6 @@ pio = namedtuple('pio', ['read_count', 'write_count',
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
||||||
|
|
||||||
def open_binary(fname, **kwargs):
|
|
||||||
return open(fname, "rb", **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def open_text(fname, **kwargs):
|
|
||||||
"""On Python 3 opens a file in text mode by using fs encoding and
|
|
||||||
a proper en/decoding errors handler.
|
|
||||||
On Python 2 this is just an alias for open(name, 'rt').
|
|
||||||
"""
|
|
||||||
if PY3:
|
|
||||||
# See:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/675
|
|
||||||
# https://github.com/giampaolo/psutil/pull/733
|
|
||||||
kwargs.setdefault('encoding', ENCODING)
|
|
||||||
kwargs.setdefault('errors', ENCODING_ERRS)
|
|
||||||
return open(fname, "rt", **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
if PY3:
|
|
||||||
def decode(s):
|
|
||||||
return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
|
|
||||||
else:
|
|
||||||
def decode(s):
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def get_procfs_path():
|
|
||||||
"""Return updated psutil.PROCFS_PATH constant."""
|
|
||||||
return sys.modules['psutil'].PROCFS_PATH
|
|
||||||
|
|
||||||
|
|
||||||
def readlink(path):
|
def readlink(path):
|
||||||
"""Wrapper around os.readlink()."""
|
"""Wrapper around os.readlink()."""
|
||||||
assert isinstance(path, basestring), path
|
assert isinstance(path, basestring), path
|
||||||
|
@ -638,6 +613,17 @@ def cpu_count_logical():
|
||||||
|
|
||||||
def cpu_count_physical():
|
def cpu_count_physical():
|
||||||
"""Return the number of physical cores in the system."""
|
"""Return the number of physical cores in the system."""
|
||||||
|
# Method #1
|
||||||
|
core_ids = set()
|
||||||
|
for path in glob.glob(
|
||||||
|
"/sys/devices/system/cpu/cpu[0-9]*/topology/core_id"):
|
||||||
|
with open_binary(path) as f:
|
||||||
|
core_ids.add(int(f.read()))
|
||||||
|
result = len(core_ids)
|
||||||
|
if result != 0:
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Method #2
|
||||||
mapping = {}
|
mapping = {}
|
||||||
current_info = {}
|
current_info = {}
|
||||||
with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
|
with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
|
||||||
|
@ -657,8 +643,8 @@ def cpu_count_physical():
|
||||||
key, value = line.split(b'\t:', 1)
|
key, value = line.split(b'\t:', 1)
|
||||||
current_info[key] = int(value)
|
current_info[key] = int(value)
|
||||||
|
|
||||||
# mimic os.cpu_count()
|
result = sum(mapping.values())
|
||||||
return sum(mapping.values()) or None
|
return result or None # mimic os.cpu_count()
|
||||||
|
|
||||||
|
|
||||||
def cpu_stats():
|
def cpu_stats():
|
||||||
|
@ -682,30 +668,26 @@ def cpu_stats():
|
||||||
ctx_switches, interrupts, soft_interrupts, syscalls)
|
ctx_switches, interrupts, soft_interrupts, syscalls)
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists("/sys/devices/system/cpu/cpufreq") or \
|
if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \
|
||||||
os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"):
|
os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"):
|
||||||
def cpu_freq():
|
def cpu_freq():
|
||||||
"""Return frequency metrics for all CPUs.
|
"""Return frequency metrics for all CPUs.
|
||||||
Contrarily to other OSes, Linux updates these values in
|
Contrarily to other OSes, Linux updates these values in
|
||||||
real-time.
|
real-time.
|
||||||
"""
|
"""
|
||||||
# scaling_* files seem preferable to cpuinfo_*, see:
|
def get_path(num):
|
||||||
# http://unix.stackexchange.com/a/87537/168884
|
for p in ("/sys/devices/system/cpu/cpufreq/policy%s" % num,
|
||||||
ret = []
|
"/sys/devices/system/cpu/cpu%s/cpufreq" % num):
|
||||||
ls = glob.glob("/sys/devices/system/cpu/cpufreq/policy*")
|
if os.path.exists(p):
|
||||||
if ls:
|
return p
|
||||||
# Sort the list so that '10' comes after '2'. This should
|
|
||||||
# ensure the CPU order is consistent with other CPU functions
|
|
||||||
# having a 'percpu' argument and returning results for multiple
|
|
||||||
# CPUs (cpu_times(), cpu_percent(), cpu_times_percent()).
|
|
||||||
ls.sort(key=lambda x: int(os.path.basename(x)[6:]))
|
|
||||||
else:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/981
|
|
||||||
ls = glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq")
|
|
||||||
ls.sort(key=lambda x: int(re.search('[0-9]+', x).group(0)))
|
|
||||||
|
|
||||||
pjoin = os.path.join
|
ret = []
|
||||||
for path in ls:
|
for n in range(cpu_count_logical()):
|
||||||
|
path = get_path(n)
|
||||||
|
if not path:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pjoin = os.path.join
|
||||||
curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None)
|
curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None)
|
||||||
if curr is None:
|
if curr is None:
|
||||||
# Likely an old RedHat, see:
|
# Likely an old RedHat, see:
|
||||||
|
@ -720,6 +702,25 @@ if os.path.exists("/sys/devices/system/cpu/cpufreq") or \
|
||||||
ret.append(_common.scpufreq(curr, min_, max_))
|
ret.append(_common.scpufreq(curr, min_, max_))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
elif os.path.exists("/proc/cpuinfo"):
|
||||||
|
def cpu_freq():
|
||||||
|
"""Alternate implementation using /proc/cpuinfo.
|
||||||
|
min and max frequencies are not available and are set to None.
|
||||||
|
"""
|
||||||
|
ret = []
|
||||||
|
with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
|
||||||
|
for line in f:
|
||||||
|
if line.lower().startswith(b'cpu mhz'):
|
||||||
|
key, value = line.split(b'\t:', 1)
|
||||||
|
ret.append(_common.scpufreq(float(value), 0., 0.))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
else:
|
||||||
|
def cpu_freq():
|
||||||
|
"""Dummy implementation when none of the above files are present.
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- network
|
# --- network
|
||||||
|
@ -1062,6 +1063,8 @@ def disk_io_counters(perdisk=False):
|
||||||
# ...unless (Linux 2.6) the line refers to a partition instead
|
# ...unless (Linux 2.6) the line refers to a partition instead
|
||||||
# of a disk, in which case the line has less fields (7):
|
# of a disk, in which case the line has less fields (7):
|
||||||
# "3 1 hda1 8 8 8 8"
|
# "3 1 hda1 8 8 8 8"
|
||||||
|
# 4.18+ has 4 fields added:
|
||||||
|
# "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0"
|
||||||
# See:
|
# See:
|
||||||
# https://www.kernel.org/doc/Documentation/iostats.txt
|
# https://www.kernel.org/doc/Documentation/iostats.txt
|
||||||
# https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
|
# https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
|
||||||
|
@ -1076,7 +1079,7 @@ def disk_io_counters(perdisk=False):
|
||||||
reads = int(fields[2])
|
reads = int(fields[2])
|
||||||
(reads_merged, rbytes, rtime, writes, writes_merged,
|
(reads_merged, rbytes, rtime, writes, writes_merged,
|
||||||
wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])
|
wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])
|
||||||
elif flen == 14:
|
elif flen == 14 or flen == 18:
|
||||||
# Linux 2.6+, line referring to a disk
|
# Linux 2.6+, line referring to a disk
|
||||||
name = fields[2]
|
name = fields[2]
|
||||||
(reads, reads_merged, rbytes, rtime, writes, writes_merged,
|
(reads, reads_merged, rbytes, rtime, writes, writes_merged,
|
||||||
|
@ -1142,7 +1145,8 @@ def disk_io_counters(perdisk=False):
|
||||||
def disk_partitions(all=False):
|
def disk_partitions(all=False):
|
||||||
"""Return mounted disk partitions as a list of namedtuples."""
|
"""Return mounted disk partitions as a list of namedtuples."""
|
||||||
fstypes = set()
|
fstypes = set()
|
||||||
with open_text("%s/filesystems" % get_procfs_path()) as f:
|
procfs_path = get_procfs_path()
|
||||||
|
with open_text("%s/filesystems" % procfs_path) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line.startswith("nodev"):
|
if not line.startswith("nodev"):
|
||||||
|
@ -1153,8 +1157,14 @@ def disk_partitions(all=False):
|
||||||
if fstype == "zfs":
|
if fstype == "zfs":
|
||||||
fstypes.add("zfs")
|
fstypes.add("zfs")
|
||||||
|
|
||||||
|
# See: https://github.com/giampaolo/psutil/issues/1307
|
||||||
|
if procfs_path == "/proc" and os.path.isfile('/etc/mtab'):
|
||||||
|
mounts_path = os.path.realpath("/etc/mtab")
|
||||||
|
else:
|
||||||
|
mounts_path = os.path.realpath("%s/self/mounts" % procfs_path)
|
||||||
|
|
||||||
retlist = []
|
retlist = []
|
||||||
partitions = cext.disk_partitions()
|
partitions = cext.disk_partitions(mounts_path)
|
||||||
for partition in partitions:
|
for partition in partitions:
|
||||||
device, mountpoint, fstype, opts = partition
|
device, mountpoint, fstype, opts = partition
|
||||||
if device == 'none':
|
if device == 'none':
|
||||||
|
@ -1229,7 +1239,51 @@ def sensors_temperatures():
|
||||||
|
|
||||||
ret[unit_name].append((label, current, high, critical))
|
ret[unit_name].append((label, current, high, critical))
|
||||||
|
|
||||||
return ret
|
# Indication that no sensors were detected in /sys/class/hwmon/
|
||||||
|
if not basenames:
|
||||||
|
basenames = glob.glob('/sys/class/thermal/thermal_zone*')
|
||||||
|
basenames = sorted(set(basenames))
|
||||||
|
|
||||||
|
for base in basenames:
|
||||||
|
try:
|
||||||
|
path = os.path.join(base, 'temp')
|
||||||
|
current = float(cat(path)) / 1000.0
|
||||||
|
path = os.path.join(base, 'type')
|
||||||
|
unit_name = cat(path, binary=False)
|
||||||
|
except (IOError, OSError, ValueError) as err:
|
||||||
|
warnings.warn("ignoring %r for file %r" % (err, path),
|
||||||
|
RuntimeWarning)
|
||||||
|
continue
|
||||||
|
|
||||||
|
trip_paths = glob.glob(base + '/trip_point*')
|
||||||
|
trip_points = set(['_'.join(
|
||||||
|
os.path.basename(p).split('_')[0:3]) for p in trip_paths])
|
||||||
|
critical = None
|
||||||
|
high = None
|
||||||
|
for trip_point in trip_points:
|
||||||
|
path = os.path.join(base, trip_point + "_type")
|
||||||
|
trip_type = cat(path, fallback='', binary=False)
|
||||||
|
if trip_type == 'critical':
|
||||||
|
critical = cat(os.path.join(base, trip_point + "_temp"),
|
||||||
|
fallback=None)
|
||||||
|
elif trip_type == 'high':
|
||||||
|
high = cat(os.path.join(base, trip_point + "_temp"),
|
||||||
|
fallback=None)
|
||||||
|
|
||||||
|
if high is not None:
|
||||||
|
try:
|
||||||
|
high = float(high) / 1000.0
|
||||||
|
except ValueError:
|
||||||
|
high = None
|
||||||
|
if critical is not None:
|
||||||
|
try:
|
||||||
|
critical = float(critical) / 1000.0
|
||||||
|
except ValueError:
|
||||||
|
critical = None
|
||||||
|
|
||||||
|
ret[unit_name].append(('', current, high, critical))
|
||||||
|
|
||||||
|
return dict(ret)
|
||||||
|
|
||||||
|
|
||||||
def sensors_fans():
|
def sensors_fans():
|
||||||
|
@ -1477,7 +1531,7 @@ def wrap_exceptions(fun):
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Linux process implementation."""
|
"""Linux process implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
|
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
|
@ -1485,13 +1539,20 @@ class Process(object):
|
||||||
self._ppid = None
|
self._ppid = None
|
||||||
self._procfs_path = get_procfs_path()
|
self._procfs_path = get_procfs_path()
|
||||||
|
|
||||||
|
def _assert_alive(self):
|
||||||
|
"""Raise NSP if the process disappeared on us."""
|
||||||
|
# For those C function who do not raise NSP, possibly returning
|
||||||
|
# incorrect or incomplete result.
|
||||||
|
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _parse_stat_file(self):
|
def _parse_stat_file(self):
|
||||||
"""Parse /proc/{pid}/stat file. Return a list of fields where
|
"""Parse /proc/{pid}/stat file and return a dict with various
|
||||||
process name is in position 0.
|
process info.
|
||||||
Using "man proc" as a reference: where "man proc" refers to
|
Using "man proc" as a reference: where "man proc" refers to
|
||||||
position N, always substract 2 (e.g starttime pos 22 in
|
position N always substract 3 (e.g ppid position 4 in
|
||||||
'man proc' == pos 20 in the list returned here).
|
'man proc' == position 1 in here).
|
||||||
The return value is cached in case oneshot() ctx manager is
|
The return value is cached in case oneshot() ctx manager is
|
||||||
in use.
|
in use.
|
||||||
"""
|
"""
|
||||||
|
@ -1502,9 +1563,23 @@ class Process(object):
|
||||||
# the first occurrence of "(" and the last occurence of ")".
|
# the first occurrence of "(" and the last occurence of ")".
|
||||||
rpar = data.rfind(b')')
|
rpar = data.rfind(b')')
|
||||||
name = data[data.find(b'(') + 1:rpar]
|
name = data[data.find(b'(') + 1:rpar]
|
||||||
others = data[rpar + 2:].split()
|
fields = data[rpar + 2:].split()
|
||||||
return [name] + others
|
|
||||||
|
|
||||||
|
ret = {}
|
||||||
|
ret['name'] = name
|
||||||
|
ret['status'] = fields[0]
|
||||||
|
ret['ppid'] = fields[1]
|
||||||
|
ret['ttynr'] = fields[4]
|
||||||
|
ret['utime'] = fields[11]
|
||||||
|
ret['stime'] = fields[12]
|
||||||
|
ret['children_utime'] = fields[13]
|
||||||
|
ret['children_stime'] = fields[14]
|
||||||
|
ret['create_time'] = fields[19]
|
||||||
|
ret['cpu_num'] = fields[36]
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _read_status_file(self):
|
def _read_status_file(self):
|
||||||
"""Read /proc/{pid}/stat file and return its content.
|
"""Read /proc/{pid}/stat file and return its content.
|
||||||
|
@ -1514,6 +1589,7 @@ class Process(object):
|
||||||
with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f:
|
with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _read_smaps_file(self):
|
def _read_smaps_file(self):
|
||||||
with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid),
|
with open_binary("%s/%s/smaps" % (self._procfs_path, self.pid),
|
||||||
|
@ -1521,18 +1597,18 @@ class Process(object):
|
||||||
return f.read().strip()
|
return f.read().strip()
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self._parse_stat_file.cache_activate()
|
self._parse_stat_file.cache_activate(self)
|
||||||
self._read_status_file.cache_activate()
|
self._read_status_file.cache_activate(self)
|
||||||
self._read_smaps_file.cache_activate()
|
self._read_smaps_file.cache_activate(self)
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self._parse_stat_file.cache_deactivate()
|
self._parse_stat_file.cache_deactivate(self)
|
||||||
self._read_status_file.cache_deactivate()
|
self._read_status_file.cache_deactivate(self)
|
||||||
self._read_smaps_file.cache_deactivate()
|
self._read_smaps_file.cache_deactivate(self)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def name(self):
|
def name(self):
|
||||||
name = self._parse_stat_file()[0]
|
name = self._parse_stat_file()['name']
|
||||||
if PY3:
|
if PY3:
|
||||||
name = decode(name)
|
name = decode(name)
|
||||||
# XXX - gets changed later and probably needs refactoring
|
# XXX - gets changed later and probably needs refactoring
|
||||||
|
@ -1574,7 +1650,7 @@ class Process(object):
|
||||||
sep = '\x00' if data.endswith('\x00') else ' '
|
sep = '\x00' if data.endswith('\x00') else ' '
|
||||||
if data.endswith(sep):
|
if data.endswith(sep):
|
||||||
data = data[:-1]
|
data = data[:-1]
|
||||||
return [x for x in data.split(sep)]
|
return data.split(sep)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def environ(self):
|
def environ(self):
|
||||||
|
@ -1584,13 +1660,14 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def terminal(self):
|
def terminal(self):
|
||||||
tty_nr = int(self._parse_stat_file()[5])
|
tty_nr = int(self._parse_stat_file()['ttynr'])
|
||||||
tmap = _psposix.get_terminal_map()
|
tmap = _psposix.get_terminal_map()
|
||||||
try:
|
try:
|
||||||
return tmap[tty_nr]
|
return tmap[tty_nr]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# May not be available on old kernels.
|
||||||
if os.path.exists('/proc/%s/io' % os.getpid()):
|
if os.path.exists('/proc/%s/io' % os.getpid()):
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def io_counters(self):
|
def io_counters(self):
|
||||||
|
@ -1601,36 +1678,41 @@ class Process(object):
|
||||||
# https://github.com/giampaolo/psutil/issues/1004
|
# https://github.com/giampaolo/psutil/issues/1004
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line:
|
if line:
|
||||||
name, value = line.split(b': ')
|
try:
|
||||||
fields[name] = int(value)
|
name, value = line.split(b': ')
|
||||||
|
except ValueError:
|
||||||
|
# https://github.com/giampaolo/psutil/issues/1004
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
fields[name] = int(value)
|
||||||
if not fields:
|
if not fields:
|
||||||
raise RuntimeError("%s file was empty" % fname)
|
raise RuntimeError("%s file was empty" % fname)
|
||||||
return pio(
|
try:
|
||||||
fields[b'syscr'], # read syscalls
|
return pio(
|
||||||
fields[b'syscw'], # write syscalls
|
fields[b'syscr'], # read syscalls
|
||||||
fields[b'read_bytes'], # read bytes
|
fields[b'syscw'], # write syscalls
|
||||||
fields[b'write_bytes'], # write bytes
|
fields[b'read_bytes'], # read bytes
|
||||||
fields[b'rchar'], # read chars
|
fields[b'write_bytes'], # write bytes
|
||||||
fields[b'wchar'], # write chars
|
fields[b'rchar'], # read chars
|
||||||
)
|
fields[b'wchar'], # write chars
|
||||||
else:
|
)
|
||||||
def io_counters(self):
|
except KeyError as err:
|
||||||
raise NotImplementedError("couldn't find /proc/%s/io (kernel "
|
raise ValueError("%r field was not found in %s; found fields "
|
||||||
"too old?)" % self.pid)
|
"are %r" % (err[0], fname, fields))
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cpu_times(self):
|
def cpu_times(self):
|
||||||
values = self._parse_stat_file()
|
values = self._parse_stat_file()
|
||||||
utime = float(values[12]) / CLOCK_TICKS
|
utime = float(values['utime']) / CLOCK_TICKS
|
||||||
stime = float(values[13]) / CLOCK_TICKS
|
stime = float(values['stime']) / CLOCK_TICKS
|
||||||
children_utime = float(values[14]) / CLOCK_TICKS
|
children_utime = float(values['children_utime']) / CLOCK_TICKS
|
||||||
children_stime = float(values[15]) / CLOCK_TICKS
|
children_stime = float(values['children_stime']) / CLOCK_TICKS
|
||||||
return _common.pcputimes(utime, stime, children_utime, children_stime)
|
return _common.pcputimes(utime, stime, children_utime, children_stime)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cpu_num(self):
|
def cpu_num(self):
|
||||||
"""What CPU the process is on."""
|
"""What CPU the process is on."""
|
||||||
return int(self._parse_stat_file()[37])
|
return int(self._parse_stat_file()['cpu_num'])
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def wait(self, timeout=None):
|
def wait(self, timeout=None):
|
||||||
|
@ -1638,14 +1720,14 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def create_time(self):
|
def create_time(self):
|
||||||
values = self._parse_stat_file()
|
ctime = float(self._parse_stat_file()['create_time'])
|
||||||
# According to documentation, starttime is in field 21 and the
|
# According to documentation, starttime is in field 21 and the
|
||||||
# unit is jiffies (clock ticks).
|
# unit is jiffies (clock ticks).
|
||||||
# We first divide it for clock ticks and then add uptime returning
|
# We first divide it for clock ticks and then add uptime returning
|
||||||
# seconds since the epoch, in UTC.
|
# seconds since the epoch, in UTC.
|
||||||
# Also use cached value if available.
|
# Also use cached value if available.
|
||||||
bt = BOOT_TIME or boot_time()
|
bt = BOOT_TIME or boot_time()
|
||||||
return (float(values[20]) / CLOCK_TICKS) + bt
|
return (ctime / CLOCK_TICKS) + bt
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def memory_info(self):
|
def memory_info(self):
|
||||||
|
@ -1707,6 +1789,9 @@ class Process(object):
|
||||||
"""Return process's mapped memory regions as a list of named
|
"""Return process's mapped memory regions as a list of named
|
||||||
tuples. Fields are explained in 'man proc'; here is an updated
|
tuples. Fields are explained in 'man proc'; here is an updated
|
||||||
(Apr 2012) version: http://goo.gl/fmebo
|
(Apr 2012) version: http://goo.gl/fmebo
|
||||||
|
|
||||||
|
/proc/{PID}/smaps does not exist on kernels < 2.6.14 or if
|
||||||
|
CONFIG_MMU kernel configuration option is not enabled.
|
||||||
"""
|
"""
|
||||||
def get_blocks(lines, current_block):
|
def get_blocks(lines, current_block):
|
||||||
data = {}
|
data = {}
|
||||||
|
@ -1767,13 +1852,6 @@ class Process(object):
|
||||||
))
|
))
|
||||||
return ls
|
return ls
|
||||||
|
|
||||||
else: # pragma: no cover
|
|
||||||
def memory_maps(self):
|
|
||||||
raise NotImplementedError(
|
|
||||||
"/proc/%s/smaps does not exist on kernels < 2.6.14 or "
|
|
||||||
"if CONFIG_MMU kernel configuration option is not "
|
|
||||||
"enabled." % self.pid)
|
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cwd(self):
|
def cwd(self):
|
||||||
try:
|
try:
|
||||||
|
@ -1836,8 +1914,7 @@ class Process(object):
|
||||||
ntuple = _common.pthread(int(thread_id), utime, stime)
|
ntuple = _common.pthread(int(thread_id), utime, stime)
|
||||||
retlist.append(ntuple)
|
retlist.append(ntuple)
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -1887,7 +1964,7 @@ class Process(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# only starting from kernel 2.6.13
|
# only starting from kernel 2.6.13
|
||||||
if hasattr(cext, "proc_ioprio_get"):
|
if HAS_PROC_IO_PRIORITY:
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def ionice_get(self):
|
def ionice_get(self):
|
||||||
|
@ -1898,38 +1975,16 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def ionice_set(self, ioclass, value):
|
def ionice_set(self, ioclass, value):
|
||||||
if value is not None:
|
if value is None:
|
||||||
if not PY3 and not isinstance(value, (int, long)):
|
|
||||||
msg = "value argument is not an integer (gor %r)" % value
|
|
||||||
raise TypeError(msg)
|
|
||||||
if not 0 <= value <= 7:
|
|
||||||
raise ValueError(
|
|
||||||
"value argument range expected is between 0 and 7")
|
|
||||||
|
|
||||||
if ioclass in (IOPRIO_CLASS_NONE, None):
|
|
||||||
if value:
|
|
||||||
msg = "can't specify value with IOPRIO_CLASS_NONE " \
|
|
||||||
"(got %r)" % value
|
|
||||||
raise ValueError(msg)
|
|
||||||
ioclass = IOPRIO_CLASS_NONE
|
|
||||||
value = 0
|
value = 0
|
||||||
elif ioclass == IOPRIO_CLASS_IDLE:
|
if value and ioclass in (IOPRIO_CLASS_IDLE, IOPRIO_CLASS_NONE):
|
||||||
if value:
|
raise ValueError("%r ioclass accepts no value" % ioclass)
|
||||||
msg = "can't specify value with IOPRIO_CLASS_IDLE " \
|
if value < 0 or value > 7:
|
||||||
"(got %r)" % value
|
raise ValueError("value not in 0-7 range")
|
||||||
raise ValueError(msg)
|
|
||||||
value = 0
|
|
||||||
elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE):
|
|
||||||
if value is None:
|
|
||||||
# TODO: add comment explaining why this is 4 (?)
|
|
||||||
value = 4
|
|
||||||
else:
|
|
||||||
# otherwise we would get OSError(EVINAL)
|
|
||||||
raise ValueError("invalid ioclass argument %r" % ioclass)
|
|
||||||
|
|
||||||
return cext.proc_ioprio_set(self.pid, ioclass, value)
|
return cext.proc_ioprio_set(self.pid, ioclass, value)
|
||||||
|
|
||||||
if HAS_PRLIMIT:
|
if HAS_PRLIMIT:
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def rlimit(self, resource, limits=None):
|
def rlimit(self, resource, limits=None):
|
||||||
# If pid is 0 prlimit() applies to the calling process and
|
# If pid is 0 prlimit() applies to the calling process and
|
||||||
|
@ -1959,7 +2014,7 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def status(self):
|
def status(self):
|
||||||
letter = self._parse_stat_file()[1]
|
letter = self._parse_stat_file()['status']
|
||||||
if PY3:
|
if PY3:
|
||||||
letter = letter.decode()
|
letter = letter.decode()
|
||||||
# XXX is '?' legit? (we're not supposed to return it anyway)
|
# XXX is '?' legit? (we're not supposed to return it anyway)
|
||||||
|
@ -1999,9 +2054,8 @@ class Process(object):
|
||||||
flags = int(f.readline().split()[1], 8)
|
flags = int(f.readline().split()[1], 8)
|
||||||
except IOError as err:
|
except IOError as err:
|
||||||
if err.errno == errno.ENOENT:
|
if err.errno == errno.ENOENT:
|
||||||
# fd gone in the meantime; does not
|
# fd gone in the meantime; process may
|
||||||
# necessarily mean the process disappeared
|
# still be alive
|
||||||
# on us.
|
|
||||||
hit_enoent = True
|
hit_enoent = True
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
@ -2011,15 +2065,13 @@ class Process(object):
|
||||||
path, int(fd), int(pos), mode, flags)
|
path, int(fd), int(pos), mode, flags)
|
||||||
retlist.append(ntuple)
|
retlist.append(ntuple)
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def connections(self, kind='inet'):
|
def connections(self, kind='inet'):
|
||||||
ret = _connections.retrieve(kind, self.pid)
|
ret = _connections.retrieve(kind, self.pid)
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -2028,7 +2080,7 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def ppid(self):
|
def ppid(self):
|
||||||
return int(self._parse_stat_file()[2])
|
return int(self._parse_stat_file()['ppid'])
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')):
|
def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')):
|
||||||
|
|
|
@ -23,9 +23,6 @@ from ._common import parse_environ_block
|
||||||
from ._common import sockfam_to_enum
|
from ._common import sockfam_to_enum
|
||||||
from ._common import socktype_to_enum
|
from ._common import socktype_to_enum
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
|
|
||||||
__extra__all__ = []
|
__extra__all__ = []
|
||||||
|
@ -87,6 +84,13 @@ pidtaskinfo_map = dict(
|
||||||
volctxsw=7,
|
volctxsw=7,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -103,13 +107,6 @@ svmem = namedtuple(
|
||||||
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
|
pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
|
||||||
# psutil.Process.memory_full_info()
|
# psutil.Process.memory_full_info()
|
||||||
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
|
pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
|
||||||
# psutil.Process.memory_maps(grouped=True)
|
|
||||||
pmmap_grouped = namedtuple(
|
|
||||||
'pmmap_grouped',
|
|
||||||
'path rss private swapped dirtied ref_count shadow_depth')
|
|
||||||
# psutil.Process.memory_maps(grouped=False)
|
|
||||||
pmmap_ext = namedtuple(
|
|
||||||
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -119,9 +116,16 @@ pmmap_ext = namedtuple(
|
||||||
|
|
||||||
def virtual_memory():
|
def virtual_memory():
|
||||||
"""System virtual memory as a namedtuple."""
|
"""System virtual memory as a namedtuple."""
|
||||||
total, active, inactive, wired, free = cext.virtual_mem()
|
total, active, inactive, wired, free, speculative = cext.virtual_mem()
|
||||||
|
# This is how Zabbix calculate avail and used mem:
|
||||||
|
# https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
|
||||||
|
# osx/memory.c
|
||||||
|
# Also see: https://github.com/giampaolo/psutil/issues/1277
|
||||||
avail = inactive + free
|
avail = inactive + free
|
||||||
used = active + inactive + wired
|
used = active + wired
|
||||||
|
# This is NOT how Zabbix calculates free mem but it matches "free"
|
||||||
|
# cmdline utility.
|
||||||
|
free -= speculative
|
||||||
percent = usage_percent((total - avail), total, round_=1)
|
percent = usage_percent((total - avail), total, round_=1)
|
||||||
return svmem(total, avail, percent, used, free,
|
return svmem(total, avail, percent, used, free,
|
||||||
active, inactive, wired)
|
active, inactive, wired)
|
||||||
|
@ -373,13 +377,14 @@ def catch_zombie(proc):
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Wrapper class around underlying C implementation."""
|
"""Wrapper class around underlying C implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid"]
|
__slots__ = ["pid", "_name", "_ppid", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self._name = None
|
self._name = None
|
||||||
self._ppid = None
|
self._ppid = None
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _get_kinfo_proc(self):
|
def _get_kinfo_proc(self):
|
||||||
# Note: should work with all PIDs without permission issues.
|
# Note: should work with all PIDs without permission issues.
|
||||||
|
@ -387,6 +392,7 @@ class Process(object):
|
||||||
assert len(ret) == len(kinfo_proc_map)
|
assert len(ret) == len(kinfo_proc_map)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _get_pidtaskinfo(self):
|
def _get_pidtaskinfo(self):
|
||||||
# Note: should work for PIDs owned by user only.
|
# Note: should work for PIDs owned by user only.
|
||||||
|
@ -396,12 +402,12 @@ class Process(object):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self._get_kinfo_proc.cache_activate()
|
self._get_kinfo_proc.cache_activate(self)
|
||||||
self._get_pidtaskinfo.cache_activate()
|
self._get_pidtaskinfo.cache_activate(self)
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self._get_kinfo_proc.cache_deactivate()
|
self._get_kinfo_proc.cache_deactivate(self)
|
||||||
self._get_pidtaskinfo.cache_deactivate()
|
self._get_pidtaskinfo.cache_deactivate(self)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -570,7 +576,3 @@ class Process(object):
|
||||||
ntuple = _common.pthread(thread_id, utime, stime)
|
ntuple = _common.pthread(thread_id, utime, stime)
|
||||||
retlist.append(ntuple)
|
retlist.append(ntuple)
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
@wrap_exceptions
|
|
||||||
def memory_maps(self):
|
|
||||||
return cext.proc_memory_maps(self.pid)
|
|
||||||
|
|
|
@ -15,12 +15,16 @@ from ._common import sdiskusage
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._compat import PY3
|
from ._compat import PY3
|
||||||
from ._compat import unicode
|
from ._compat import unicode
|
||||||
from ._exceptions import TimeoutExpired
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
|
__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
|
||||||
|
|
||||||
|
|
||||||
|
# This object gets set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
def pid_exists(pid):
|
def pid_exists(pid):
|
||||||
"""Check whether pid exists in the current process table."""
|
"""Check whether pid exists in the current process table."""
|
||||||
if pid == 0:
|
if pid == 0:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"""Sun OS Solaris platform implementation."""
|
"""Sun OS Solaris platform implementation."""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -17,6 +18,7 @@ from . import _psposix
|
||||||
from . import _psutil_posix as cext_posix
|
from . import _psutil_posix as cext_posix
|
||||||
from . import _psutil_sunos as cext
|
from . import _psutil_sunos as cext
|
||||||
from ._common import AF_INET6
|
from ._common import AF_INET6
|
||||||
|
from ._common import get_procfs_path
|
||||||
from ._common import isfile_strict
|
from ._common import isfile_strict
|
||||||
from ._common import memoize_when_activated
|
from ._common import memoize_when_activated
|
||||||
from ._common import sockfam_to_enum
|
from ._common import sockfam_to_enum
|
||||||
|
@ -24,9 +26,6 @@ from ._common import socktype_to_enum
|
||||||
from ._common import usage_percent
|
from ._common import usage_percent
|
||||||
from ._compat import b
|
from ._compat import b
|
||||||
from ._compat import PY3
|
from ._compat import PY3
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import ZombieProcess
|
|
||||||
|
|
||||||
|
|
||||||
__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
|
__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
|
||||||
|
@ -85,6 +84,13 @@ proc_info_map = dict(
|
||||||
gid=10,
|
gid=10,
|
||||||
egid=11)
|
egid=11)
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -109,16 +115,6 @@ pmmap_ext = namedtuple(
|
||||||
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
|
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- utils
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def get_procfs_path():
|
|
||||||
"""Return updated psutil.PROCFS_PATH constant."""
|
|
||||||
return sys.modules['psutil'].PROCFS_PATH
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- memory
|
# --- memory
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -341,7 +337,7 @@ def wrap_exceptions(fun):
|
||||||
"""Call callable into a try/except clause and translate ENOENT,
|
"""Call callable into a try/except clause and translate ENOENT,
|
||||||
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
|
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
|
||||||
"""
|
"""
|
||||||
|
@functools.wraps(fun)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return fun(self, *args, **kwargs)
|
return fun(self, *args, **kwargs)
|
||||||
|
@ -368,7 +364,7 @@ def wrap_exceptions(fun):
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Wrapper class around underlying C implementation."""
|
"""Wrapper class around underlying C implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
|
__slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
|
@ -376,32 +372,38 @@ class Process(object):
|
||||||
self._ppid = None
|
self._ppid = None
|
||||||
self._procfs_path = get_procfs_path()
|
self._procfs_path = get_procfs_path()
|
||||||
|
|
||||||
|
def _assert_alive(self):
|
||||||
|
"""Raise NSP if the process disappeared on us."""
|
||||||
|
# For those C function who do not raise NSP, possibly returning
|
||||||
|
# incorrect or incomplete result.
|
||||||
|
os.stat('%s/%s' % (self._procfs_path, self.pid))
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self._proc_name_and_args.cache_activate()
|
self._proc_name_and_args.cache_activate(self)
|
||||||
self._proc_basic_info.cache_activate()
|
self._proc_basic_info.cache_activate(self)
|
||||||
self._proc_cred.cache_activate()
|
self._proc_cred.cache_activate(self)
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self._proc_name_and_args.cache_deactivate()
|
self._proc_name_and_args.cache_deactivate(self)
|
||||||
self._proc_basic_info.cache_deactivate()
|
self._proc_basic_info.cache_deactivate(self)
|
||||||
self._proc_cred.cache_deactivate()
|
self._proc_cred.cache_deactivate(self)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _proc_name_and_args(self):
|
def _proc_name_and_args(self):
|
||||||
return cext.proc_name_and_args(self.pid, self._procfs_path)
|
return cext.proc_name_and_args(self.pid, self._procfs_path)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _proc_basic_info(self):
|
def _proc_basic_info(self):
|
||||||
ret = cext.proc_basic_info(self.pid, self._procfs_path)
|
ret = cext.proc_basic_info(self.pid, self._procfs_path)
|
||||||
assert len(ret) == len(proc_info_map)
|
assert len(ret) == len(proc_info_map)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def _proc_cred(self):
|
def _proc_cred(self):
|
||||||
@wrap_exceptions
|
return cext.proc_cred(self.pid, self._procfs_path)
|
||||||
def proc_cred(self):
|
|
||||||
return cext.proc_cred(self.pid, self._procfs_path)
|
|
||||||
return proc_cred(self)
|
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -518,8 +520,7 @@ class Process(object):
|
||||||
continue
|
continue
|
||||||
raise
|
raise
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (procfs_path, self.pid))
|
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cwd(self):
|
def cwd(self):
|
||||||
|
@ -581,8 +582,7 @@ class Process(object):
|
||||||
nt = _common.pthread(tid, utime, stime)
|
nt = _common.pthread(tid, utime, stime)
|
||||||
ret.append(nt)
|
ret.append(nt)
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (procfs_path, self.pid))
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -606,8 +606,7 @@ class Process(object):
|
||||||
if isfile_strict(file):
|
if isfile_strict(file):
|
||||||
retlist.append(_common.popenfile(file, int(fd)))
|
retlist.append(_common.popenfile(file, int(fd)))
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (procfs_path, self.pid))
|
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
def _get_unix_sockets(self, pid):
|
def _get_unix_sockets(self, pid):
|
||||||
|
@ -707,8 +706,7 @@ class Process(object):
|
||||||
raise
|
raise
|
||||||
retlist.append((addr, perm, name, rss, anon, locked))
|
retlist.append((addr, perm, name, rss, anon, locked))
|
||||||
if hit_enoent:
|
if hit_enoent:
|
||||||
# raise NSP if the process disappeared on us
|
self._assert_alive()
|
||||||
os.stat('%s/%s' % (procfs_path, self.pid))
|
|
||||||
return retlist
|
return retlist
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -27,8 +27,7 @@ except ImportError as err:
|
||||||
# but if we get here it means this this was a wheel (or exe).
|
# but if we get here it means this this was a wheel (or exe).
|
||||||
msg = "this Windows version is too old (< Windows Vista); "
|
msg = "this Windows version is too old (< Windows Vista); "
|
||||||
msg += "psutil 3.4.2 is the latest version which supports Windows "
|
msg += "psutil 3.4.2 is the latest version which supports Windows "
|
||||||
msg += "2000, XP and 2003 server; it may be possible that psutil "
|
msg += "2000, XP and 2003 server"
|
||||||
msg += "will work if compiled from sources though"
|
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
@ -37,6 +36,7 @@ from ._common import conn_tmap
|
||||||
from ._common import ENCODING
|
from ._common import ENCODING
|
||||||
from ._common import ENCODING_ERRS
|
from ._common import ENCODING_ERRS
|
||||||
from ._common import isfile_strict
|
from ._common import isfile_strict
|
||||||
|
from ._common import memoize
|
||||||
from ._common import memoize_when_activated
|
from ._common import memoize_when_activated
|
||||||
from ._common import parse_environ_block
|
from ._common import parse_environ_block
|
||||||
from ._common import sockfam_to_enum
|
from ._common import sockfam_to_enum
|
||||||
|
@ -47,9 +47,6 @@ from ._compat import lru_cache
|
||||||
from ._compat import PY3
|
from ._compat import PY3
|
||||||
from ._compat import unicode
|
from ._compat import unicode
|
||||||
from ._compat import xrange
|
from ._compat import xrange
|
||||||
from ._exceptions import AccessDenied
|
|
||||||
from ._exceptions import NoSuchProcess
|
|
||||||
from ._exceptions import TimeoutExpired
|
|
||||||
from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
|
from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
|
||||||
from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
|
from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
|
||||||
from ._psutil_windows import HIGH_PRIORITY_CLASS
|
from ._psutil_windows import HIGH_PRIORITY_CLASS
|
||||||
|
@ -66,11 +63,14 @@ else:
|
||||||
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
|
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
|
||||||
__extra__all__ = [
|
__extra__all__ = [
|
||||||
"win_service_iter", "win_service_get",
|
"win_service_iter", "win_service_get",
|
||||||
|
# Process priority
|
||||||
"ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
|
"ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
|
||||||
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
|
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS",
|
||||||
"NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
|
"REALTIME_PRIORITY_CLASS",
|
||||||
"CONN_DELETE_TCB",
|
# IO priority
|
||||||
"AF_LINK",
|
"IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH",
|
||||||
|
# others
|
||||||
|
"CONN_DELETE_TCB", "AF_LINK",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,10 +79,8 @@ __extra__all__ = [
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
|
||||||
CONN_DELETE_TCB = "DELETE_TCB"
|
CONN_DELETE_TCB = "DELETE_TCB"
|
||||||
ACCESS_DENIED_ERRSET = frozenset([errno.EPERM, errno.EACCES,
|
HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_io_priority_get")
|
||||||
cext.ERROR_ACCESS_DENIED])
|
HAS_GETLOADAVG = hasattr(cext, "getloadavg")
|
||||||
NO_SUCH_SERVICE_ERRSET = frozenset([cext.ERROR_INVALID_NAME,
|
|
||||||
cext.ERROR_SERVICE_DOES_NOT_EXIST])
|
|
||||||
|
|
||||||
|
|
||||||
if enum is None:
|
if enum is None:
|
||||||
|
@ -118,6 +116,19 @@ if enum is not None:
|
||||||
|
|
||||||
globals().update(Priority.__members__)
|
globals().update(Priority.__members__)
|
||||||
|
|
||||||
|
if enum is None:
|
||||||
|
IOPRIO_VERYLOW = 0
|
||||||
|
IOPRIO_LOW = 1
|
||||||
|
IOPRIO_NORMAL = 2
|
||||||
|
IOPRIO_HIGH = 3
|
||||||
|
else:
|
||||||
|
class IOPriority(enum.IntEnum):
|
||||||
|
IOPRIO_VERYLOW = 0
|
||||||
|
IOPRIO_LOW = 1
|
||||||
|
IOPRIO_NORMAL = 2
|
||||||
|
IOPRIO_HIGH = 3
|
||||||
|
globals().update(IOPriority.__members__)
|
||||||
|
|
||||||
pinfo_map = dict(
|
pinfo_map = dict(
|
||||||
num_handles=0,
|
num_handles=0,
|
||||||
ctx_switches=1,
|
ctx_switches=1,
|
||||||
|
@ -143,6 +154,35 @@ pinfo_map = dict(
|
||||||
mem_private=21,
|
mem_private=21,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# These objects get set on "import psutil" from the __init__.py
|
||||||
|
# file, see: https://github.com/giampaolo/psutil/issues/1402
|
||||||
|
NoSuchProcess = None
|
||||||
|
ZombieProcess = None
|
||||||
|
AccessDenied = None
|
||||||
|
TimeoutExpired = None
|
||||||
|
|
||||||
|
# More values at: https://stackoverflow.com/a/20804735/376587
|
||||||
|
WIN_10 = (10, 0)
|
||||||
|
WIN_8 = (6, 2)
|
||||||
|
WIN_7 = (6, 1)
|
||||||
|
WIN_SERVER_2008 = (6, 0)
|
||||||
|
WIN_VISTA = (6, 0)
|
||||||
|
WIN_SERVER_2003 = (5, 2)
|
||||||
|
WIN_XP = (5, 1)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_winver():
|
||||||
|
"""Usage:
|
||||||
|
>>> if get_winver() <= WIN_VISTA:
|
||||||
|
... ...
|
||||||
|
"""
|
||||||
|
wv = sys.getwindowsversion()
|
||||||
|
return (wv.major, wv.minor)
|
||||||
|
|
||||||
|
|
||||||
|
IS_WIN_XP = get_winver() < WIN_VISTA
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- named tuples
|
# --- named tuples
|
||||||
|
@ -203,6 +243,11 @@ def py2_strencode(s):
|
||||||
return s.encode(ENCODING, ENCODING_ERRS)
|
return s.encode(ENCODING, ENCODING_ERRS)
|
||||||
|
|
||||||
|
|
||||||
|
@memoize
|
||||||
|
def getpagesize():
|
||||||
|
return cext.getpagesize()
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- memory
|
# --- memory
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -309,6 +354,23 @@ def cpu_freq():
|
||||||
return [_common.scpufreq(float(curr), min_, float(max_))]
|
return [_common.scpufreq(float(curr), min_, float(max_))]
|
||||||
|
|
||||||
|
|
||||||
|
if HAS_GETLOADAVG:
|
||||||
|
_loadavg_inititialized = False
|
||||||
|
|
||||||
|
def getloadavg():
|
||||||
|
"""Return the number of processes in the system run queue averaged
|
||||||
|
over the last 1, 5, and 15 minutes respectively as a tuple"""
|
||||||
|
global _loadavg_inititialized
|
||||||
|
|
||||||
|
if not _loadavg_inititialized:
|
||||||
|
cext.init_loadavg_counter()
|
||||||
|
_loadavg_inititialized = True
|
||||||
|
|
||||||
|
# Drop to 2 decimal points which is what Linux does
|
||||||
|
raw_loads = cext.getloadavg()
|
||||||
|
return tuple([round(load, 2) for load in raw_loads])
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# --- network
|
# --- network
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
|
@ -501,14 +563,14 @@ class WindowsService(object):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
except WindowsError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if is_permission_err(err):
|
||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
pid=None, name=self._name,
|
pid=None, name=self._name,
|
||||||
msg="service %r is not querable (not enough privileges)" %
|
msg="service %r is not querable (not enough privileges)" %
|
||||||
self._name)
|
self._name)
|
||||||
elif err.errno in NO_SUCH_SERVICE_ERRSET or \
|
elif err.winerror in (cext.ERROR_INVALID_NAME,
|
||||||
err.winerror in NO_SUCH_SERVICE_ERRSET:
|
cext.ERROR_SERVICE_DOES_NOT_EXIST):
|
||||||
raise NoSuchProcess(
|
raise NoSuchProcess(
|
||||||
pid=None, name=self._name,
|
pid=None, name=self._name,
|
||||||
msg="service %r does not exist)" % self._name)
|
msg="service %r does not exist)" % self._name)
|
||||||
|
@ -625,27 +687,42 @@ pid_exists = cext.pid_exists
|
||||||
ppid_map = cext.ppid_map # used internally by Process.children()
|
ppid_map = cext.ppid_map # used internally by Process.children()
|
||||||
|
|
||||||
|
|
||||||
|
def is_permission_err(exc):
|
||||||
|
"""Return True if this is a permission error."""
|
||||||
|
assert isinstance(exc, OSError), exc
|
||||||
|
# On Python 2 OSError doesn't always have 'winerror'. Sometimes
|
||||||
|
# it does, in which case the original exception was WindowsError
|
||||||
|
# (which is a subclass of OSError).
|
||||||
|
return exc.errno in (errno.EPERM, errno.EACCES) or \
|
||||||
|
getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED,
|
||||||
|
cext.ERROR_PRIVILEGE_NOT_HELD)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_oserror(exc, pid=None, name=None):
|
||||||
|
"""Convert OSError into NoSuchProcess or AccessDenied."""
|
||||||
|
assert isinstance(exc, OSError), exc
|
||||||
|
if is_permission_err(exc):
|
||||||
|
return AccessDenied(pid=pid, name=name)
|
||||||
|
if exc.errno == errno.ESRCH:
|
||||||
|
return NoSuchProcess(pid=pid, name=name)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
def wrap_exceptions(fun):
|
def wrap_exceptions(fun):
|
||||||
"""Decorator which translates bare OSError and WindowsError
|
"""Decorator which converts OSError into NoSuchProcess or AccessDenied."""
|
||||||
exceptions into NoSuchProcess and AccessDenied.
|
|
||||||
"""
|
|
||||||
@functools.wraps(fun)
|
@functools.wraps(fun)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return fun(self, *args, **kwargs)
|
return fun(self, *args, **kwargs)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
raise convert_oserror(err, pid=self.pid, name=self._name)
|
||||||
raise AccessDenied(self.pid, self._name)
|
|
||||||
if err.errno == errno.ESRCH:
|
|
||||||
raise NoSuchProcess(self.pid, self._name)
|
|
||||||
raise
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Process(object):
|
class Process(object):
|
||||||
"""Wrapper class around underlying C implementation."""
|
"""Wrapper class around underlying C implementation."""
|
||||||
|
|
||||||
__slots__ = ["pid", "_name", "_ppid"]
|
__slots__ = ["pid", "_name", "_ppid", "_cache"]
|
||||||
|
|
||||||
def __init__(self, pid):
|
def __init__(self, pid):
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
|
@ -655,11 +732,12 @@ class Process(object):
|
||||||
# --- oneshot() stuff
|
# --- oneshot() stuff
|
||||||
|
|
||||||
def oneshot_enter(self):
|
def oneshot_enter(self):
|
||||||
self.oneshot_info.cache_activate()
|
self.oneshot_info.cache_activate(self)
|
||||||
|
|
||||||
def oneshot_exit(self):
|
def oneshot_exit(self):
|
||||||
self.oneshot_info.cache_deactivate()
|
self.oneshot_info.cache_deactivate(self)
|
||||||
|
|
||||||
|
@wrap_exceptions
|
||||||
@memoize_when_activated
|
@memoize_when_activated
|
||||||
def oneshot_info(self):
|
def oneshot_info(self):
|
||||||
"""Return multiple information about this process as a
|
"""Return multiple information about this process as a
|
||||||
|
@ -690,19 +768,33 @@ class Process(object):
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def exe(self):
|
def exe(self):
|
||||||
# Note: os.path.exists(path) may return False even if the file
|
# Dual implementation, see:
|
||||||
# is there, see:
|
# https://github.com/giampaolo/psutil/pull/1413
|
||||||
# http://stackoverflow.com/questions/3112546/os-path-exists-lies
|
if not IS_WIN_XP:
|
||||||
|
exe = cext.proc_exe(self.pid)
|
||||||
# see https://github.com/giampaolo/psutil/issues/414
|
else:
|
||||||
# see https://github.com/giampaolo/psutil/issues/528
|
if self.pid in (0, 4):
|
||||||
if self.pid in (0, 4):
|
# https://github.com/giampaolo/psutil/issues/414
|
||||||
raise AccessDenied(self.pid, self._name)
|
# https://github.com/giampaolo/psutil/issues/528
|
||||||
return py2_strencode(convert_dos_path(cext.proc_exe(self.pid)))
|
raise AccessDenied(self.pid, self._name)
|
||||||
|
exe = cext.proc_exe(self.pid)
|
||||||
|
exe = convert_dos_path(exe)
|
||||||
|
return py2_strencode(exe)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cmdline(self):
|
def cmdline(self):
|
||||||
ret = cext.proc_cmdline(self.pid)
|
if cext.WINVER >= cext.WINDOWS_8_1:
|
||||||
|
# PEB method detects cmdline changes but requires more
|
||||||
|
# privileges: https://github.com/giampaolo/psutil/pull/1398
|
||||||
|
try:
|
||||||
|
ret = cext.proc_cmdline(self.pid, use_peb=True)
|
||||||
|
except OSError as err:
|
||||||
|
if is_permission_err(err):
|
||||||
|
ret = cext.proc_cmdline(self.pid, use_peb=False)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
ret = cext.proc_cmdline(self.pid, use_peb=True)
|
||||||
if PY3:
|
if PY3:
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
|
@ -725,7 +817,7 @@ class Process(object):
|
||||||
try:
|
try:
|
||||||
return cext.proc_memory_info(self.pid)
|
return cext.proc_memory_info(self.pid)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if is_permission_err(err):
|
||||||
# TODO: the C ext can probably be refactored in order
|
# TODO: the C ext can probably be refactored in order
|
||||||
# to get this from cext.proc_info()
|
# to get this from cext.proc_info()
|
||||||
info = self.oneshot_info()
|
info = self.oneshot_info()
|
||||||
|
@ -757,6 +849,7 @@ class Process(object):
|
||||||
def memory_full_info(self):
|
def memory_full_info(self):
|
||||||
basic_mem = self.memory_info()
|
basic_mem = self.memory_info()
|
||||||
uss = cext.proc_memory_uss(self.pid)
|
uss = cext.proc_memory_uss(self.pid)
|
||||||
|
uss *= getpagesize()
|
||||||
return pfullmem(*basic_mem + (uss, ))
|
return pfullmem(*basic_mem + (uss, ))
|
||||||
|
|
||||||
def memory_maps(self):
|
def memory_maps(self):
|
||||||
|
@ -765,11 +858,7 @@ class Process(object):
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
# XXX - can't use wrap_exceptions decorator as we're
|
# XXX - can't use wrap_exceptions decorator as we're
|
||||||
# returning a generator; probably needs refactoring.
|
# returning a generator; probably needs refactoring.
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
raise convert_oserror(err, self.pid, self._name)
|
||||||
raise AccessDenied(self.pid, self._name)
|
|
||||||
if err.errno == errno.ESRCH:
|
|
||||||
raise NoSuchProcess(self.pid, self._name)
|
|
||||||
raise
|
|
||||||
else:
|
else:
|
||||||
for addr, perm, path, rss in raw:
|
for addr, perm, path, rss in raw:
|
||||||
path = convert_dos_path(path)
|
path = convert_dos_path(path)
|
||||||
|
@ -845,7 +934,7 @@ class Process(object):
|
||||||
try:
|
try:
|
||||||
return cext.proc_create_time(self.pid)
|
return cext.proc_create_time(self.pid)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if is_permission_err(err):
|
||||||
return self.oneshot_info()[pinfo_map['create_time']]
|
return self.oneshot_info()[pinfo_map['create_time']]
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -867,22 +956,21 @@ class Process(object):
|
||||||
try:
|
try:
|
||||||
user, system = cext.proc_cpu_times(self.pid)
|
user, system = cext.proc_cpu_times(self.pid)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if not is_permission_err(err):
|
||||||
info = self.oneshot_info()
|
|
||||||
user = info[pinfo_map['user_time']]
|
|
||||||
system = info[pinfo_map['kernel_time']]
|
|
||||||
else:
|
|
||||||
raise
|
raise
|
||||||
|
info = self.oneshot_info()
|
||||||
|
user = info[pinfo_map['user_time']]
|
||||||
|
system = info[pinfo_map['kernel_time']]
|
||||||
# Children user/system times are not retrievable (set to 0).
|
# Children user/system times are not retrievable (set to 0).
|
||||||
return _common.pcputimes(user, system, 0.0, 0.0)
|
return _common.pcputimes(user, system, 0.0, 0.0)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def suspend(self):
|
def suspend(self):
|
||||||
return cext.proc_suspend(self.pid)
|
cext.proc_suspend_or_resume(self.pid, True)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def resume(self):
|
def resume(self):
|
||||||
return cext.proc_resume(self.pid)
|
cext.proc_suspend_or_resume(self.pid, False)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def cwd(self):
|
def cwd(self):
|
||||||
|
@ -928,38 +1016,39 @@ class Process(object):
|
||||||
return cext.proc_priority_set(self.pid, value)
|
return cext.proc_priority_set(self.pid, value)
|
||||||
|
|
||||||
# available on Windows >= Vista
|
# available on Windows >= Vista
|
||||||
if hasattr(cext, "proc_io_priority_get"):
|
if HAS_PROC_IO_PRIORITY:
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def ionice_get(self):
|
def ionice_get(self):
|
||||||
return cext.proc_io_priority_get(self.pid)
|
ret = cext.proc_io_priority_get(self.pid)
|
||||||
|
if enum is not None:
|
||||||
|
ret = IOPriority(ret)
|
||||||
|
return ret
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def ionice_set(self, value, _):
|
def ionice_set(self, ioclass, value):
|
||||||
if _:
|
if value:
|
||||||
raise TypeError("set_proc_ionice() on Windows takes only "
|
raise TypeError("value argument not accepted on Windows")
|
||||||
"1 argument (2 given)")
|
if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL,
|
||||||
if value not in (2, 1, 0):
|
IOPRIO_HIGH):
|
||||||
raise ValueError("value must be 2 (normal), 1 (low) or 0 "
|
raise ValueError("%s is not a valid priority" % ioclass)
|
||||||
"(very low); got %r" % value)
|
cext.proc_io_priority_set(self.pid, ioclass)
|
||||||
return cext.proc_io_priority_set(self.pid, value)
|
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
def io_counters(self):
|
def io_counters(self):
|
||||||
try:
|
try:
|
||||||
ret = cext.proc_io_counters(self.pid)
|
ret = cext.proc_io_counters(self.pid)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if not is_permission_err(err):
|
||||||
info = self.oneshot_info()
|
|
||||||
ret = (
|
|
||||||
info[pinfo_map['io_rcount']],
|
|
||||||
info[pinfo_map['io_wcount']],
|
|
||||||
info[pinfo_map['io_rbytes']],
|
|
||||||
info[pinfo_map['io_wbytes']],
|
|
||||||
info[pinfo_map['io_count_others']],
|
|
||||||
info[pinfo_map['io_bytes_others']],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise
|
raise
|
||||||
|
info = self.oneshot_info()
|
||||||
|
ret = (
|
||||||
|
info[pinfo_map['io_rcount']],
|
||||||
|
info[pinfo_map['io_wcount']],
|
||||||
|
info[pinfo_map['io_rbytes']],
|
||||||
|
info[pinfo_map['io_wbytes']],
|
||||||
|
info[pinfo_map['io_count_others']],
|
||||||
|
info[pinfo_map['io_bytes_others']],
|
||||||
|
)
|
||||||
return pio(*ret)
|
return pio(*ret)
|
||||||
|
|
||||||
@wrap_exceptions
|
@wrap_exceptions
|
||||||
|
@ -1007,7 +1096,7 @@ class Process(object):
|
||||||
try:
|
try:
|
||||||
return cext.proc_num_handles(self.pid)
|
return cext.proc_num_handles(self.pid)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
if err.errno in ACCESS_DENIED_ERRSET:
|
if is_permission_err(err):
|
||||||
return self.oneshot_info()[pinfo_map['num_handles']]
|
return self.oneshot_info()[pinfo_map['num_handles']]
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,94 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Run unit tests. This is invoked by:
|
|
||||||
|
|
||||||
$ python -m psutil.tests
|
|
||||||
"""
|
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
try:
|
|
||||||
from urllib.request import urlopen # py3
|
|
||||||
except ImportError:
|
|
||||||
from urllib2 import urlopen
|
|
||||||
|
|
||||||
from psutil.tests import PYTHON_EXE
|
|
||||||
from psutil.tests import run_suite
|
|
||||||
|
|
||||||
|
|
||||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
|
|
||||||
TEST_DEPS = []
|
|
||||||
if sys.version_info[:2] == (2, 6):
|
|
||||||
TEST_DEPS.extend(["ipaddress", "unittest2", "argparse", "mock==1.0.1"])
|
|
||||||
elif sys.version_info[:2] == (2, 7) or sys.version_info[:2] <= (3, 2):
|
|
||||||
TEST_DEPS.extend(["ipaddress", "mock"])
|
|
||||||
|
|
||||||
|
|
||||||
def install_pip():
|
|
||||||
try:
|
|
||||||
import pip # NOQA
|
|
||||||
except ImportError:
|
|
||||||
import ssl
|
|
||||||
f = tempfile.NamedTemporaryFile(suffix='.py')
|
|
||||||
with contextlib.closing(f):
|
|
||||||
print("downloading %s to %s" % (GET_PIP_URL, f.name))
|
|
||||||
if hasattr(ssl, '_create_unverified_context'):
|
|
||||||
ctx = ssl._create_unverified_context()
|
|
||||||
else:
|
|
||||||
ctx = None
|
|
||||||
kwargs = dict(context=ctx) if ctx else {}
|
|
||||||
req = urlopen(GET_PIP_URL, **kwargs)
|
|
||||||
data = req.read()
|
|
||||||
f.write(data)
|
|
||||||
f.flush()
|
|
||||||
|
|
||||||
print("installing pip")
|
|
||||||
code = os.system('%s %s --user' % (PYTHON_EXE, f.name))
|
|
||||||
return code
|
|
||||||
|
|
||||||
|
|
||||||
def install_test_deps(deps=None):
|
|
||||||
"""Install test dependencies via pip."""
|
|
||||||
if deps is None:
|
|
||||||
deps = TEST_DEPS
|
|
||||||
deps = set(deps)
|
|
||||||
if deps:
|
|
||||||
is_venv = hasattr(sys, 'real_prefix')
|
|
||||||
opts = "--user" if not is_venv else ""
|
|
||||||
install_pip()
|
|
||||||
code = os.system('%s -m pip install %s --upgrade %s' % (
|
|
||||||
PYTHON_EXE, opts, " ".join(deps)))
|
|
||||||
return code
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usage = "%s -m psutil.tests [opts]" % PYTHON_EXE
|
|
||||||
parser = optparse.OptionParser(usage=usage, description="run unit tests")
|
|
||||||
parser.add_option("-i", "--install-deps",
|
|
||||||
action="store_true", default=False,
|
|
||||||
help="don't print status messages to stdout")
|
|
||||||
|
|
||||||
opts, args = parser.parse_args()
|
|
||||||
if opts.install_deps:
|
|
||||||
install_pip()
|
|
||||||
install_test_deps()
|
|
||||||
else:
|
|
||||||
for dep in TEST_DEPS:
|
|
||||||
try:
|
|
||||||
__import__(dep.split("==")[0])
|
|
||||||
except ImportError:
|
|
||||||
sys.exit("%r lib is not installed; run %s -m psutil.tests "
|
|
||||||
"--install-deps" % (dep, PYTHON_EXE))
|
|
||||||
run_suite()
|
|
||||||
|
|
||||||
|
|
||||||
main()
|
|
|
@ -1,121 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'
|
|
||||||
# Copyright (c) 2017, Arnon Yaari
|
|
||||||
# All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""AIX specific tests."""
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from psutil import AIX
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import unittest
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not AIX, "AIX only")
|
|
||||||
class AIXSpecificTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_virtual_memory(self):
|
|
||||||
out = sh('/usr/bin/svmon -O unit=KB')
|
|
||||||
re_pattern = "memory\s*"
|
|
||||||
for field in ("size inuse free pin virtual available mmode").split():
|
|
||||||
re_pattern += "(?P<%s>\S+)\s+" % (field,)
|
|
||||||
matchobj = re.search(re_pattern, out)
|
|
||||||
|
|
||||||
self.assertIsNotNone(
|
|
||||||
matchobj, "svmon command returned unexpected output")
|
|
||||||
|
|
||||||
KB = 1024
|
|
||||||
total = int(matchobj.group("size")) * KB
|
|
||||||
available = int(matchobj.group("available")) * KB
|
|
||||||
used = int(matchobj.group("inuse")) * KB
|
|
||||||
free = int(matchobj.group("free")) * KB
|
|
||||||
|
|
||||||
psutil_result = psutil.virtual_memory()
|
|
||||||
|
|
||||||
# MEMORY_TOLERANCE from psutil.tests is not enough. For some reason
|
|
||||||
# we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance
|
|
||||||
# when compared to GBs.
|
|
||||||
MEMORY_TOLERANCE = 2 * KB * KB # 2 MB
|
|
||||||
self.assertEqual(psutil_result.total, total)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.used, used, delta=MEMORY_TOLERANCE)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.available, available, delta=MEMORY_TOLERANCE)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.free, free, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swap_memory(self):
|
|
||||||
out = sh('/usr/sbin/lsps -a')
|
|
||||||
# From the man page, "The size is given in megabytes" so we assume
|
|
||||||
# we'll always have 'MB' in the result
|
|
||||||
# TODO maybe try to use "swap -l" to check "used" too, but its units
|
|
||||||
# are not guaranteed to be "MB" so parsing may not be consistent
|
|
||||||
matchobj = re.search("(?P<space>\S+)\s+"
|
|
||||||
"(?P<vol>\S+)\s+"
|
|
||||||
"(?P<vg>\S+)\s+"
|
|
||||||
"(?P<size>\d+)MB", out)
|
|
||||||
|
|
||||||
self.assertIsNotNone(
|
|
||||||
matchobj, "lsps command returned unexpected output")
|
|
||||||
|
|
||||||
total_mb = int(matchobj.group("size"))
|
|
||||||
MB = 1024 ** 2
|
|
||||||
psutil_result = psutil.swap_memory()
|
|
||||||
# we divide our result by MB instead of multiplying the lsps value by
|
|
||||||
# MB because lsps may round down, so we round down too
|
|
||||||
self.assertEqual(int(psutil_result.total / MB), total_mb)
|
|
||||||
|
|
||||||
def test_cpu_stats(self):
|
|
||||||
out = sh('/usr/bin/mpstat -a')
|
|
||||||
|
|
||||||
re_pattern = "ALL\s*"
|
|
||||||
for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq "
|
|
||||||
"push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd "
|
|
||||||
"sysc").split():
|
|
||||||
re_pattern += "(?P<%s>\S+)\s+" % (field,)
|
|
||||||
matchobj = re.search(re_pattern, out)
|
|
||||||
|
|
||||||
self.assertIsNotNone(
|
|
||||||
matchobj, "mpstat command returned unexpected output")
|
|
||||||
|
|
||||||
# numbers are usually in the millions so 1000 is ok for tolerance
|
|
||||||
CPU_STATS_TOLERANCE = 1000
|
|
||||||
psutil_result = psutil.cpu_stats()
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.ctx_switches,
|
|
||||||
int(matchobj.group("cs")),
|
|
||||||
delta=CPU_STATS_TOLERANCE)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.syscalls,
|
|
||||||
int(matchobj.group("sysc")),
|
|
||||||
delta=CPU_STATS_TOLERANCE)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.interrupts,
|
|
||||||
int(matchobj.group("dev")),
|
|
||||||
delta=CPU_STATS_TOLERANCE)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil_result.soft_interrupts,
|
|
||||||
int(matchobj.group("soft")),
|
|
||||||
delta=CPU_STATS_TOLERANCE)
|
|
||||||
|
|
||||||
def test_cpu_count_logical(self):
|
|
||||||
out = sh('/usr/bin/mpstat -a')
|
|
||||||
mpstat_lcpu = int(re.search("lcpu=(\d+)", out).group(1))
|
|
||||||
psutil_lcpu = psutil.cpu_count(logical=True)
|
|
||||||
self.assertEqual(mpstat_lcpu, psutil_lcpu)
|
|
||||||
|
|
||||||
def test_net_if_addrs_names(self):
|
|
||||||
out = sh('/etc/ifconfig -l')
|
|
||||||
ifconfig_names = set(out.split())
|
|
||||||
psutil_names = set(psutil.net_if_addrs().keys())
|
|
||||||
self.assertSetEqual(ifconfig_names, psutil_names)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,519 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# TODO: (FreeBSD) add test for comparing connections with 'sockstat' cmd.
|
|
||||||
|
|
||||||
|
|
||||||
"""Tests specific to all BSD platforms."""
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import BSD
|
|
||||||
from psutil import FREEBSD
|
|
||||||
from psutil import NETBSD
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_BATTERY
|
|
||||||
from psutil.tests import MEMORY_TOLERANCE
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import retry_before_failing
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import unittest
|
|
||||||
from psutil.tests import which
|
|
||||||
|
|
||||||
|
|
||||||
if BSD:
|
|
||||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
|
|
||||||
if os.getuid() == 0: # muse requires root privileges
|
|
||||||
MUSE_AVAILABLE = which('muse')
|
|
||||||
else:
|
|
||||||
MUSE_AVAILABLE = False
|
|
||||||
else:
|
|
||||||
MUSE_AVAILABLE = False
|
|
||||||
|
|
||||||
|
|
||||||
def sysctl(cmdline):
|
|
||||||
"""Expects a sysctl command with an argument and parse the result
|
|
||||||
returning only the value of interest.
|
|
||||||
"""
|
|
||||||
result = sh("sysctl " + cmdline)
|
|
||||||
if FREEBSD:
|
|
||||||
result = result[result.find(": ") + 2:]
|
|
||||||
elif OPENBSD or NETBSD:
|
|
||||||
result = result[result.find("=") + 1:]
|
|
||||||
try:
|
|
||||||
return int(result)
|
|
||||||
except ValueError:
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def muse(field):
|
|
||||||
"""Thin wrapper around 'muse' cmdline utility."""
|
|
||||||
out = sh('muse')
|
|
||||||
for line in out.split('\n'):
|
|
||||||
if line.startswith(field):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("line not found")
|
|
||||||
return int(line.split()[1])
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- All BSD*
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not BSD, "BSD only")
|
|
||||||
class BSDSpecificTestCase(unittest.TestCase):
|
|
||||||
"""Generic tests common to all BSD variants."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
@unittest.skipIf(NETBSD, "-o lstart doesn't work on NETBSD")
|
|
||||||
def test_process_create_time(self):
|
|
||||||
output = sh("ps -o lstart -p %s" % self.pid)
|
|
||||||
start_ps = output.replace('STARTED', '').strip()
|
|
||||||
start_psutil = psutil.Process(self.pid).create_time()
|
|
||||||
start_psutil = time.strftime("%a %b %e %H:%M:%S %Y",
|
|
||||||
time.localtime(start_psutil))
|
|
||||||
self.assertEqual(start_ps, start_psutil)
|
|
||||||
|
|
||||||
def test_disks(self):
|
|
||||||
# test psutil.disk_usage() and psutil.disk_partitions()
|
|
||||||
# against "df -a"
|
|
||||||
def df(path):
|
|
||||||
out = sh('df -k "%s"' % path).strip()
|
|
||||||
lines = out.split('\n')
|
|
||||||
lines.pop(0)
|
|
||||||
line = lines.pop(0)
|
|
||||||
dev, total, used, free = line.split()[:4]
|
|
||||||
if dev == 'none':
|
|
||||||
dev = ''
|
|
||||||
total = int(total) * 1024
|
|
||||||
used = int(used) * 1024
|
|
||||||
free = int(free) * 1024
|
|
||||||
return dev, total, used, free
|
|
||||||
|
|
||||||
for part in psutil.disk_partitions(all=False):
|
|
||||||
usage = psutil.disk_usage(part.mountpoint)
|
|
||||||
dev, total, used, free = df(part.mountpoint)
|
|
||||||
self.assertEqual(part.device, dev)
|
|
||||||
self.assertEqual(usage.total, total)
|
|
||||||
# 10 MB tollerance
|
|
||||||
if abs(usage.free - free) > 10 * 1024 * 1024:
|
|
||||||
self.fail("psutil=%s, df=%s" % (usage.free, free))
|
|
||||||
if abs(usage.used - used) > 10 * 1024 * 1024:
|
|
||||||
self.fail("psutil=%s, df=%s" % (usage.used, used))
|
|
||||||
|
|
||||||
@unittest.skipIf(not which('sysctl'), "sysctl cmd not available")
|
|
||||||
def test_cpu_count_logical(self):
|
|
||||||
syst = sysctl("hw.ncpu")
|
|
||||||
self.assertEqual(psutil.cpu_count(logical=True), syst)
|
|
||||||
|
|
||||||
@unittest.skipIf(not which('sysctl'), "sysctl cmd not available")
|
|
||||||
def test_virtual_memory_total(self):
|
|
||||||
num = sysctl('hw.physmem')
|
|
||||||
self.assertEqual(num, psutil.virtual_memory().total)
|
|
||||||
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
for name, stats in psutil.net_if_stats().items():
|
|
||||||
try:
|
|
||||||
out = sh("ifconfig %s" % name)
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
|
|
||||||
if "mtu" in out:
|
|
||||||
self.assertEqual(stats.mtu,
|
|
||||||
int(re.findall(r'mtu (\d+)', out)[0]))
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- FreeBSD
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not FREEBSD, "FREEBSD only")
|
|
||||||
class FreeBSDSpecificTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_swapinfo():
|
|
||||||
# the last line is always the total
|
|
||||||
output = sh("swapinfo -k").splitlines()[-1]
|
|
||||||
parts = re.split(r'\s+', output)
|
|
||||||
|
|
||||||
if not parts:
|
|
||||||
raise ValueError("Can't parse swapinfo: %s" % output)
|
|
||||||
|
|
||||||
# the size is in 1k units, so multiply by 1024
|
|
||||||
total, used, free = (int(p) * 1024 for p in parts[1:4])
|
|
||||||
return total, used, free
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_proc_memory_maps(self):
|
|
||||||
out = sh('procstat -v %s' % self.pid)
|
|
||||||
maps = psutil.Process(self.pid).memory_maps(grouped=False)
|
|
||||||
lines = out.split('\n')[1:]
|
|
||||||
while lines:
|
|
||||||
line = lines.pop()
|
|
||||||
fields = line.split()
|
|
||||||
_, start, stop, perms, res = fields[:5]
|
|
||||||
map = maps.pop()
|
|
||||||
self.assertEqual("%s-%s" % (start, stop), map.addr)
|
|
||||||
self.assertEqual(int(res), map.rss)
|
|
||||||
if not map.path.startswith('['):
|
|
||||||
self.assertEqual(fields[10], map.path)
|
|
||||||
|
|
||||||
def test_proc_exe(self):
|
|
||||||
out = sh('procstat -b %s' % self.pid)
|
|
||||||
self.assertEqual(psutil.Process(self.pid).exe(),
|
|
||||||
out.split('\n')[1].split()[-1])
|
|
||||||
|
|
||||||
def test_proc_cmdline(self):
|
|
||||||
out = sh('procstat -c %s' % self.pid)
|
|
||||||
self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()),
|
|
||||||
' '.join(out.split('\n')[1].split()[2:]))
|
|
||||||
|
|
||||||
def test_proc_uids_gids(self):
|
|
||||||
out = sh('procstat -s %s' % self.pid)
|
|
||||||
euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
uids = p.uids()
|
|
||||||
gids = p.gids()
|
|
||||||
self.assertEqual(uids.real, int(ruid))
|
|
||||||
self.assertEqual(uids.effective, int(euid))
|
|
||||||
self.assertEqual(uids.saved, int(suid))
|
|
||||||
self.assertEqual(gids.real, int(rgid))
|
|
||||||
self.assertEqual(gids.effective, int(egid))
|
|
||||||
self.assertEqual(gids.saved, int(sgid))
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_proc_ctx_switches(self):
|
|
||||||
tested = []
|
|
||||||
out = sh('procstat -r %s' % self.pid)
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
for line in out.split('\n'):
|
|
||||||
line = line.lower().strip()
|
|
||||||
if ' voluntary context' in line:
|
|
||||||
pstat_value = int(line.split()[-1])
|
|
||||||
psutil_value = p.num_ctx_switches().voluntary
|
|
||||||
self.assertEqual(pstat_value, psutil_value)
|
|
||||||
tested.append(None)
|
|
||||||
elif ' involuntary context' in line:
|
|
||||||
pstat_value = int(line.split()[-1])
|
|
||||||
psutil_value = p.num_ctx_switches().involuntary
|
|
||||||
self.assertEqual(pstat_value, psutil_value)
|
|
||||||
tested.append(None)
|
|
||||||
if len(tested) != 2:
|
|
||||||
raise RuntimeError("couldn't find lines match in procstat out")
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_proc_cpu_times(self):
|
|
||||||
tested = []
|
|
||||||
out = sh('procstat -r %s' % self.pid)
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
for line in out.split('\n'):
|
|
||||||
line = line.lower().strip()
|
|
||||||
if 'user time' in line:
|
|
||||||
pstat_value = float('0.' + line.split()[-1].split('.')[-1])
|
|
||||||
psutil_value = p.cpu_times().user
|
|
||||||
self.assertEqual(pstat_value, psutil_value)
|
|
||||||
tested.append(None)
|
|
||||||
elif 'system time' in line:
|
|
||||||
pstat_value = float('0.' + line.split()[-1].split('.')[-1])
|
|
||||||
psutil_value = p.cpu_times().system
|
|
||||||
self.assertEqual(pstat_value, psutil_value)
|
|
||||||
tested.append(None)
|
|
||||||
if len(tested) != 2:
|
|
||||||
raise RuntimeError("couldn't find lines match in procstat out")
|
|
||||||
|
|
||||||
# --- virtual_memory(); tests against sysctl
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_active(self):
|
|
||||||
syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().active, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_inactive(self):
|
|
||||||
syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().inactive, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_wired(self):
|
|
||||||
syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().wired, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_cached(self):
|
|
||||||
syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().cached, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_free(self):
|
|
||||||
syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().free, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_buffers(self):
|
|
||||||
syst = sysctl("vfs.bufspace")
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().buffers, syst,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
# --- virtual_memory(); tests against muse
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
def test_muse_vmem_total(self):
|
|
||||||
num = muse('Total')
|
|
||||||
self.assertEqual(psutil.virtual_memory().total, num)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_active(self):
|
|
||||||
num = muse('Active')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().active, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_inactive(self):
|
|
||||||
num = muse('Inactive')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().inactive, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_wired(self):
|
|
||||||
num = muse('Wired')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().wired, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_cached(self):
|
|
||||||
num = muse('Cache')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().cached, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_free(self):
|
|
||||||
num = muse('Free')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().free, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not MUSE_AVAILABLE, "muse not installed")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_muse_vmem_buffers(self):
|
|
||||||
num = muse('Buffer')
|
|
||||||
self.assertAlmostEqual(psutil.virtual_memory().buffers, num,
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_cpu_stats_ctx_switches(self):
|
|
||||||
self.assertAlmostEqual(psutil.cpu_stats().ctx_switches,
|
|
||||||
sysctl('vm.stats.sys.v_swtch'), delta=1000)
|
|
||||||
|
|
||||||
def test_cpu_stats_interrupts(self):
|
|
||||||
self.assertAlmostEqual(psutil.cpu_stats().interrupts,
|
|
||||||
sysctl('vm.stats.sys.v_intr'), delta=1000)
|
|
||||||
|
|
||||||
def test_cpu_stats_soft_interrupts(self):
|
|
||||||
self.assertAlmostEqual(psutil.cpu_stats().soft_interrupts,
|
|
||||||
sysctl('vm.stats.sys.v_soft'), delta=1000)
|
|
||||||
|
|
||||||
def test_cpu_stats_syscalls(self):
|
|
||||||
self.assertAlmostEqual(psutil.cpu_stats().syscalls,
|
|
||||||
sysctl('vm.stats.sys.v_syscall'), delta=1000)
|
|
||||||
|
|
||||||
# def test_cpu_stats_traps(self):
|
|
||||||
# self.assertAlmostEqual(psutil.cpu_stats().traps,
|
|
||||||
# sysctl('vm.stats.sys.v_trap'), delta=1000)
|
|
||||||
|
|
||||||
# --- swap memory
|
|
||||||
|
|
||||||
def test_swapmem_free(self):
|
|
||||||
total, used, free = self.parse_swapinfo()
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.swap_memory().free, free, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swapmem_used(self):
|
|
||||||
total, used, free = self.parse_swapinfo()
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.swap_memory().used, used, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swapmem_total(self):
|
|
||||||
total, used, free = self.parse_swapinfo()
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.swap_memory().total, total, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
# --- others
|
|
||||||
|
|
||||||
def test_boot_time(self):
|
|
||||||
s = sysctl('sysctl kern.boottime')
|
|
||||||
s = s[s.find(" sec = ") + 7:]
|
|
||||||
s = s[:s.find(',')]
|
|
||||||
btime = int(s)
|
|
||||||
self.assertEqual(btime, psutil.boot_time())
|
|
||||||
|
|
||||||
# --- sensors_battery
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_sensors_battery(self):
|
|
||||||
def secs2hours(secs):
|
|
||||||
m, s = divmod(secs, 60)
|
|
||||||
h, m = divmod(m, 60)
|
|
||||||
return "%d:%02d" % (h, m)
|
|
||||||
|
|
||||||
out = sh("acpiconf -i 0")
|
|
||||||
fields = dict([(x.split('\t')[0], x.split('\t')[-1])
|
|
||||||
for x in out.split("\n")])
|
|
||||||
metrics = psutil.sensors_battery()
|
|
||||||
percent = int(fields['Remaining capacity:'].replace('%', ''))
|
|
||||||
remaining_time = fields['Remaining time:']
|
|
||||||
self.assertEqual(metrics.percent, percent)
|
|
||||||
if remaining_time == 'unknown':
|
|
||||||
self.assertEqual(metrics.secsleft, psutil.POWER_TIME_UNLIMITED)
|
|
||||||
else:
|
|
||||||
self.assertEqual(secs2hours(metrics.secsleft), remaining_time)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_sensors_battery_against_sysctl(self):
|
|
||||||
self.assertEqual(psutil.sensors_battery().percent,
|
|
||||||
sysctl("hw.acpi.battery.life"))
|
|
||||||
self.assertEqual(psutil.sensors_battery().power_plugged,
|
|
||||||
sysctl("hw.acpi.acline") == 1)
|
|
||||||
secsleft = psutil.sensors_battery().secsleft
|
|
||||||
if secsleft < 0:
|
|
||||||
self.assertEqual(sysctl("hw.acpi.battery.time"), -1)
|
|
||||||
else:
|
|
||||||
self.assertEqual(secsleft, sysctl("hw.acpi.battery.time") * 60)
|
|
||||||
|
|
||||||
@unittest.skipIf(HAS_BATTERY, "has battery")
|
|
||||||
def test_sensors_battery_no_battery(self):
|
|
||||||
# If no battery is present one of these calls is supposed
|
|
||||||
# to fail, see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1074
|
|
||||||
with self.assertRaises(RuntimeError):
|
|
||||||
sysctl("hw.acpi.battery.life")
|
|
||||||
sysctl("hw.acpi.battery.time")
|
|
||||||
sysctl("hw.acpi.acline")
|
|
||||||
self.assertIsNone(psutil.sensors_battery())
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- OpenBSD
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not OPENBSD, "OPENBSD only")
|
|
||||||
class OpenBSDSpecificTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_boot_time(self):
|
|
||||||
s = sysctl('kern.boottime')
|
|
||||||
sys_bt = datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y")
|
|
||||||
psutil_bt = datetime.datetime.fromtimestamp(psutil.boot_time())
|
|
||||||
self.assertEqual(sys_bt, psutil_bt)
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- NetBSD
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not NETBSD, "NETBSD only")
|
|
||||||
class NetBSDSpecificTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse_meminfo(look_for):
|
|
||||||
with open('/proc/meminfo', 'rb') as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith(look_for):
|
|
||||||
return int(line.split()[1]) * 1024
|
|
||||||
raise ValueError("can't find %s" % look_for)
|
|
||||||
|
|
||||||
def test_vmem_total(self):
|
|
||||||
self.assertEqual(
|
|
||||||
psutil.virtual_memory().total, self.parse_meminfo("MemTotal:"))
|
|
||||||
|
|
||||||
def test_vmem_free(self):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.virtual_memory().free, self.parse_meminfo("MemFree:"),
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_vmem_buffers(self):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.virtual_memory().buffers, self.parse_meminfo("Buffers:"),
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_vmem_shared(self):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.virtual_memory().shared, self.parse_meminfo("MemShared:"),
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swapmem_total(self):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.swap_memory().total, self.parse_meminfo("SwapTotal:"),
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swapmem_free(self):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.swap_memory().free, self.parse_meminfo("SwapFree:"),
|
|
||||||
delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
def test_swapmem_used(self):
|
|
||||||
smem = psutil.swap_memory()
|
|
||||||
self.assertEqual(smem.used, smem.total - smem.free)
|
|
||||||
|
|
||||||
def test_cpu_stats_interrupts(self):
|
|
||||||
with open('/proc/stat', 'rb') as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith(b'intr'):
|
|
||||||
interrupts = int(line.split()[1])
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("couldn't find line")
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.cpu_stats().interrupts, interrupts, delta=1000)
|
|
||||||
|
|
||||||
def test_cpu_stats_ctx_switches(self):
|
|
||||||
with open('/proc/stat', 'rb') as f:
|
|
||||||
for line in f:
|
|
||||||
if line.startswith(b'ctxt'):
|
|
||||||
ctx_switches = int(line.split()[1])
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("couldn't find line")
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
psutil.cpu_stats().ctx_switches, ctx_switches, delta=1000)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,525 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Tests for net_connections() and Process.connections() APIs."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import textwrap
|
|
||||||
from contextlib import closing
|
|
||||||
from socket import AF_INET
|
|
||||||
from socket import AF_INET6
|
|
||||||
from socket import SOCK_DGRAM
|
|
||||||
from socket import SOCK_STREAM
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import FREEBSD
|
|
||||||
from psutil import LINUX
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import NETBSD
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil._common import supports_ipv6
|
|
||||||
from psutil._compat import PY3
|
|
||||||
from psutil.tests import AF_UNIX
|
|
||||||
from psutil.tests import bind_socket
|
|
||||||
from psutil.tests import bind_unix_socket
|
|
||||||
from psutil.tests import check_connection_ntuple
|
|
||||||
from psutil.tests import create_sockets
|
|
||||||
from psutil.tests import get_free_port
|
|
||||||
from psutil.tests import HAS_CONNECTIONS_UNIX
|
|
||||||
from psutil.tests import pyrun
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import safe_rmpath
|
|
||||||
from psutil.tests import skip_on_access_denied
|
|
||||||
from psutil.tests import tcp_socketpair
|
|
||||||
from psutil.tests import TESTFN
|
|
||||||
from psutil.tests import TRAVIS
|
|
||||||
from psutil.tests import unittest
|
|
||||||
from psutil.tests import unix_socket_path
|
|
||||||
from psutil.tests import unix_socketpair
|
|
||||||
from psutil.tests import wait_for_file
|
|
||||||
|
|
||||||
|
|
||||||
thisproc = psutil.Process()
|
|
||||||
|
|
||||||
|
|
||||||
class Base(object):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
if not NETBSD:
|
|
||||||
# NetBSD opens a UNIX socket to /var/log/run.
|
|
||||||
cons = thisproc.connections(kind='all')
|
|
||||||
assert not cons, cons
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
safe_rmpath(TESTFN)
|
|
||||||
reap_children()
|
|
||||||
if not NETBSD:
|
|
||||||
# Make sure we closed all resources.
|
|
||||||
# NetBSD opens a UNIX socket to /var/log/run.
|
|
||||||
cons = thisproc.connections(kind='all')
|
|
||||||
assert not cons, cons
|
|
||||||
|
|
||||||
def get_conn_from_sock(self, sock):
|
|
||||||
cons = thisproc.connections(kind='all')
|
|
||||||
smap = dict([(c.fd, c) for c in cons])
|
|
||||||
if NETBSD:
|
|
||||||
# NetBSD opens a UNIX socket to /var/log/run
|
|
||||||
# so there may be more connections.
|
|
||||||
return smap[sock.fileno()]
|
|
||||||
else:
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
if cons[0].fd != -1:
|
|
||||||
self.assertEqual(smap[sock.fileno()].fd, sock.fileno())
|
|
||||||
return cons[0]
|
|
||||||
|
|
||||||
def check_socket(self, sock, conn=None):
|
|
||||||
"""Given a socket, makes sure it matches the one obtained
|
|
||||||
via psutil. It assumes this process created one connection
|
|
||||||
only (the one supposed to be checked).
|
|
||||||
"""
|
|
||||||
if conn is None:
|
|
||||||
conn = self.get_conn_from_sock(sock)
|
|
||||||
check_connection_ntuple(conn)
|
|
||||||
|
|
||||||
# fd, family, type
|
|
||||||
if conn.fd != -1:
|
|
||||||
self.assertEqual(conn.fd, sock.fileno())
|
|
||||||
self.assertEqual(conn.family, sock.family)
|
|
||||||
# see: http://bugs.python.org/issue30204
|
|
||||||
self.assertEqual(
|
|
||||||
conn.type, sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE))
|
|
||||||
|
|
||||||
# local address
|
|
||||||
laddr = sock.getsockname()
|
|
||||||
if not laddr and PY3 and isinstance(laddr, bytes):
|
|
||||||
# See: http://bugs.python.org/issue30205
|
|
||||||
laddr = laddr.decode()
|
|
||||||
if sock.family == AF_INET6:
|
|
||||||
laddr = laddr[:2]
|
|
||||||
if sock.family == AF_UNIX and OPENBSD:
|
|
||||||
# No addresses are set for UNIX sockets on OpenBSD.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.assertEqual(conn.laddr, laddr)
|
|
||||||
|
|
||||||
# XXX Solaris can't retrieve system-wide UNIX sockets
|
|
||||||
if sock.family == AF_UNIX and HAS_CONNECTIONS_UNIX:
|
|
||||||
cons = thisproc.connections(kind='all')
|
|
||||||
self.compare_procsys_connections(os.getpid(), cons)
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def compare_procsys_connections(self, pid, proc_cons, kind='all'):
|
|
||||||
"""Given a process PID and its list of connections compare
|
|
||||||
those against system-wide connections retrieved via
|
|
||||||
psutil.net_connections.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
sys_cons = psutil.net_connections(kind=kind)
|
|
||||||
except psutil.AccessDenied:
|
|
||||||
# On MACOS, system-wide connections are retrieved by iterating
|
|
||||||
# over all processes
|
|
||||||
if MACOS:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
# Filter for this proc PID and exlucde PIDs from the tuple.
|
|
||||||
sys_cons = [c[:-1] for c in sys_cons if c.pid == pid]
|
|
||||||
sys_cons.sort()
|
|
||||||
proc_cons.sort()
|
|
||||||
self.assertEqual(proc_cons, sys_cons)
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- Test unconnected sockets
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestUnconnectedSockets(Base, unittest.TestCase):
|
|
||||||
"""Tests sockets which are open but not connected to anything."""
|
|
||||||
|
|
||||||
def test_tcp_v4(self):
|
|
||||||
addr = ("127.0.0.1", get_free_port())
|
|
||||||
with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_LISTEN)
|
|
||||||
|
|
||||||
@unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
|
|
||||||
def test_tcp_v6(self):
|
|
||||||
addr = ("::1", get_free_port())
|
|
||||||
with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_LISTEN)
|
|
||||||
|
|
||||||
def test_udp_v4(self):
|
|
||||||
addr = ("127.0.0.1", get_free_port())
|
|
||||||
with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
|
|
||||||
def test_udp_v6(self):
|
|
||||||
addr = ("::1", get_free_port())
|
|
||||||
with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
|
||||||
def test_unix_tcp(self):
|
|
||||||
with unix_socket_path() as name:
|
|
||||||
with closing(bind_unix_socket(name, type=SOCK_STREAM)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
|
||||||
def test_unix_udp(self):
|
|
||||||
with unix_socket_path() as name:
|
|
||||||
with closing(bind_unix_socket(name, type=SOCK_STREAM)) as sock:
|
|
||||||
conn = self.check_socket(sock)
|
|
||||||
assert not conn.raddr
|
|
||||||
self.assertEqual(conn.status, psutil.CONN_NONE)
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- Test connected sockets
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestConnectedSocketPairs(Base, unittest.TestCase):
|
|
||||||
"""Test socket pairs which are are actually connected to
|
|
||||||
each other.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# On SunOS, even after we close() it, the server socket stays around
|
|
||||||
# in TIME_WAIT state.
|
|
||||||
@unittest.skipIf(SUNOS, "unreliable on SUONS")
|
|
||||||
def test_tcp(self):
|
|
||||||
addr = ("127.0.0.1", get_free_port())
|
|
||||||
assert not thisproc.connections(kind='tcp4')
|
|
||||||
server, client = tcp_socketpair(AF_INET, addr=addr)
|
|
||||||
try:
|
|
||||||
cons = thisproc.connections(kind='tcp4')
|
|
||||||
self.assertEqual(len(cons), 2)
|
|
||||||
self.assertEqual(cons[0].status, psutil.CONN_ESTABLISHED)
|
|
||||||
self.assertEqual(cons[1].status, psutil.CONN_ESTABLISHED)
|
|
||||||
# May not be fast enough to change state so it stays
|
|
||||||
# commenteed.
|
|
||||||
# client.close()
|
|
||||||
# cons = thisproc.connections(kind='all')
|
|
||||||
# self.assertEqual(len(cons), 1)
|
|
||||||
# self.assertEqual(cons[0].status, psutil.CONN_CLOSE_WAIT)
|
|
||||||
finally:
|
|
||||||
server.close()
|
|
||||||
client.close()
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
|
||||||
def test_unix(self):
|
|
||||||
with unix_socket_path() as name:
|
|
||||||
server, client = unix_socketpair(name)
|
|
||||||
try:
|
|
||||||
cons = thisproc.connections(kind='unix')
|
|
||||||
assert not (cons[0].laddr and cons[0].raddr)
|
|
||||||
assert not (cons[1].laddr and cons[1].raddr)
|
|
||||||
if NETBSD:
|
|
||||||
# On NetBSD creating a UNIX socket will cause
|
|
||||||
# a UNIX connection to /var/run/log.
|
|
||||||
cons = [c for c in cons if c.raddr != '/var/run/log']
|
|
||||||
self.assertEqual(len(cons), 2)
|
|
||||||
if LINUX or FREEBSD or SUNOS:
|
|
||||||
# remote path is never set
|
|
||||||
self.assertEqual(cons[0].raddr, "")
|
|
||||||
self.assertEqual(cons[1].raddr, "")
|
|
||||||
# one local address should though
|
|
||||||
self.assertEqual(name, cons[0].laddr or cons[1].laddr)
|
|
||||||
elif OPENBSD:
|
|
||||||
# No addresses whatsoever here.
|
|
||||||
for addr in (cons[0].laddr, cons[0].raddr,
|
|
||||||
cons[1].laddr, cons[1].raddr):
|
|
||||||
self.assertEqual(addr, "")
|
|
||||||
else:
|
|
||||||
# On other systems either the laddr or raddr
|
|
||||||
# of both peers are set.
|
|
||||||
self.assertEqual(cons[0].laddr or cons[1].laddr, name)
|
|
||||||
self.assertEqual(cons[0].raddr or cons[1].raddr, name)
|
|
||||||
finally:
|
|
||||||
server.close()
|
|
||||||
client.close()
|
|
||||||
|
|
||||||
@skip_on_access_denied(only_if=MACOS)
|
|
||||||
def test_combos(self):
|
|
||||||
def check_conn(proc, conn, family, type, laddr, raddr, status, kinds):
|
|
||||||
all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4",
|
|
||||||
"tcp6", "udp", "udp4", "udp6")
|
|
||||||
check_connection_ntuple(conn)
|
|
||||||
self.assertEqual(conn.family, family)
|
|
||||||
self.assertEqual(conn.type, type)
|
|
||||||
self.assertEqual(conn.laddr, laddr)
|
|
||||||
self.assertEqual(conn.raddr, raddr)
|
|
||||||
self.assertEqual(conn.status, status)
|
|
||||||
for kind in all_kinds:
|
|
||||||
cons = proc.connections(kind=kind)
|
|
||||||
if kind in kinds:
|
|
||||||
assert cons
|
|
||||||
else:
|
|
||||||
assert not cons, cons
|
|
||||||
# compare against system-wide connections
|
|
||||||
# XXX Solaris can't retrieve system-wide UNIX
|
|
||||||
# sockets.
|
|
||||||
if HAS_CONNECTIONS_UNIX:
|
|
||||||
self.compare_procsys_connections(proc.pid, [conn])
|
|
||||||
|
|
||||||
tcp_template = textwrap.dedent("""
|
|
||||||
import socket, time
|
|
||||||
s = socket.socket($family, socket.SOCK_STREAM)
|
|
||||||
s.bind(('$addr', 0))
|
|
||||||
s.listen(1)
|
|
||||||
with open('$testfn', 'w') as f:
|
|
||||||
f.write(str(s.getsockname()[:2]))
|
|
||||||
time.sleep(60)
|
|
||||||
""")
|
|
||||||
|
|
||||||
udp_template = textwrap.dedent("""
|
|
||||||
import socket, time
|
|
||||||
s = socket.socket($family, socket.SOCK_DGRAM)
|
|
||||||
s.bind(('$addr', 0))
|
|
||||||
with open('$testfn', 'w') as f:
|
|
||||||
f.write(str(s.getsockname()[:2]))
|
|
||||||
time.sleep(60)
|
|
||||||
""")
|
|
||||||
|
|
||||||
from string import Template
|
|
||||||
testfile = os.path.basename(TESTFN)
|
|
||||||
tcp4_template = Template(tcp_template).substitute(
|
|
||||||
family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
|
|
||||||
udp4_template = Template(udp_template).substitute(
|
|
||||||
family=int(AF_INET), addr="127.0.0.1", testfn=testfile)
|
|
||||||
tcp6_template = Template(tcp_template).substitute(
|
|
||||||
family=int(AF_INET6), addr="::1", testfn=testfile)
|
|
||||||
udp6_template = Template(udp_template).substitute(
|
|
||||||
family=int(AF_INET6), addr="::1", testfn=testfile)
|
|
||||||
|
|
||||||
# launch various subprocess instantiating a socket of various
|
|
||||||
# families and types to enrich psutil results
|
|
||||||
tcp4_proc = pyrun(tcp4_template)
|
|
||||||
tcp4_addr = eval(wait_for_file(testfile))
|
|
||||||
udp4_proc = pyrun(udp4_template)
|
|
||||||
udp4_addr = eval(wait_for_file(testfile))
|
|
||||||
if supports_ipv6():
|
|
||||||
tcp6_proc = pyrun(tcp6_template)
|
|
||||||
tcp6_addr = eval(wait_for_file(testfile))
|
|
||||||
udp6_proc = pyrun(udp6_template)
|
|
||||||
udp6_addr = eval(wait_for_file(testfile))
|
|
||||||
else:
|
|
||||||
tcp6_proc = None
|
|
||||||
udp6_proc = None
|
|
||||||
tcp6_addr = None
|
|
||||||
udp6_addr = None
|
|
||||||
|
|
||||||
for p in thisproc.children():
|
|
||||||
cons = p.connections()
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
for conn in cons:
|
|
||||||
# TCP v4
|
|
||||||
if p.pid == tcp4_proc.pid:
|
|
||||||
check_conn(p, conn, AF_INET, SOCK_STREAM, tcp4_addr, (),
|
|
||||||
psutil.CONN_LISTEN,
|
|
||||||
("all", "inet", "inet4", "tcp", "tcp4"))
|
|
||||||
# UDP v4
|
|
||||||
elif p.pid == udp4_proc.pid:
|
|
||||||
check_conn(p, conn, AF_INET, SOCK_DGRAM, udp4_addr, (),
|
|
||||||
psutil.CONN_NONE,
|
|
||||||
("all", "inet", "inet4", "udp", "udp4"))
|
|
||||||
# TCP v6
|
|
||||||
elif p.pid == getattr(tcp6_proc, "pid", None):
|
|
||||||
check_conn(p, conn, AF_INET6, SOCK_STREAM, tcp6_addr, (),
|
|
||||||
psutil.CONN_LISTEN,
|
|
||||||
("all", "inet", "inet6", "tcp", "tcp6"))
|
|
||||||
# UDP v6
|
|
||||||
elif p.pid == getattr(udp6_proc, "pid", None):
|
|
||||||
check_conn(p, conn, AF_INET6, SOCK_DGRAM, udp6_addr, (),
|
|
||||||
psutil.CONN_NONE,
|
|
||||||
("all", "inet", "inet6", "udp", "udp6"))
|
|
||||||
|
|
||||||
# err
|
|
||||||
self.assertRaises(ValueError, p.connections, kind='???')
|
|
||||||
|
|
||||||
def test_multi_sockets_filtering(self):
|
|
||||||
with create_sockets() as socks:
|
|
||||||
cons = thisproc.connections(kind='all')
|
|
||||||
self.assertEqual(len(cons), len(socks))
|
|
||||||
# tcp
|
|
||||||
cons = thisproc.connections(kind='tcp')
|
|
||||||
self.assertEqual(len(cons), 2 if supports_ipv6() else 1)
|
|
||||||
for conn in cons:
|
|
||||||
self.assertIn(conn.family, (AF_INET, AF_INET6))
|
|
||||||
self.assertEqual(conn.type, SOCK_STREAM)
|
|
||||||
# tcp4
|
|
||||||
cons = thisproc.connections(kind='tcp4')
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
self.assertEqual(cons[0].family, AF_INET)
|
|
||||||
self.assertEqual(cons[0].type, SOCK_STREAM)
|
|
||||||
# tcp6
|
|
||||||
if supports_ipv6():
|
|
||||||
cons = thisproc.connections(kind='tcp6')
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
self.assertEqual(cons[0].family, AF_INET6)
|
|
||||||
self.assertEqual(cons[0].type, SOCK_STREAM)
|
|
||||||
# udp
|
|
||||||
cons = thisproc.connections(kind='udp')
|
|
||||||
self.assertEqual(len(cons), 2 if supports_ipv6() else 1)
|
|
||||||
for conn in cons:
|
|
||||||
self.assertIn(conn.family, (AF_INET, AF_INET6))
|
|
||||||
self.assertEqual(conn.type, SOCK_DGRAM)
|
|
||||||
# udp4
|
|
||||||
cons = thisproc.connections(kind='udp4')
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
self.assertEqual(cons[0].family, AF_INET)
|
|
||||||
self.assertEqual(cons[0].type, SOCK_DGRAM)
|
|
||||||
# udp6
|
|
||||||
if supports_ipv6():
|
|
||||||
cons = thisproc.connections(kind='udp6')
|
|
||||||
self.assertEqual(len(cons), 1)
|
|
||||||
self.assertEqual(cons[0].family, AF_INET6)
|
|
||||||
self.assertEqual(cons[0].type, SOCK_DGRAM)
|
|
||||||
# inet
|
|
||||||
cons = thisproc.connections(kind='inet')
|
|
||||||
self.assertEqual(len(cons), 4 if supports_ipv6() else 2)
|
|
||||||
for conn in cons:
|
|
||||||
self.assertIn(conn.family, (AF_INET, AF_INET6))
|
|
||||||
self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
|
|
||||||
# inet6
|
|
||||||
if supports_ipv6():
|
|
||||||
cons = thisproc.connections(kind='inet6')
|
|
||||||
self.assertEqual(len(cons), 2)
|
|
||||||
for conn in cons:
|
|
||||||
self.assertEqual(conn.family, AF_INET6)
|
|
||||||
self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
|
|
||||||
# unix
|
|
||||||
if HAS_CONNECTIONS_UNIX:
|
|
||||||
cons = thisproc.connections(kind='unix')
|
|
||||||
self.assertEqual(len(cons), 3)
|
|
||||||
for conn in cons:
|
|
||||||
self.assertEqual(conn.family, AF_UNIX)
|
|
||||||
self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- Miscellaneous tests
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestSystemWideConnections(Base, unittest.TestCase):
|
|
||||||
"""Tests for net_connections()."""
|
|
||||||
|
|
||||||
@skip_on_access_denied()
|
|
||||||
def test_it(self):
|
|
||||||
def check(cons, families, types_):
|
|
||||||
AF_UNIX = getattr(socket, 'AF_UNIX', object())
|
|
||||||
for conn in cons:
|
|
||||||
self.assertIn(conn.family, families, msg=conn)
|
|
||||||
if conn.family != AF_UNIX:
|
|
||||||
self.assertIn(conn.type, types_, msg=conn)
|
|
||||||
check_connection_ntuple(conn)
|
|
||||||
|
|
||||||
with create_sockets():
|
|
||||||
from psutil._common import conn_tmap
|
|
||||||
for kind, groups in conn_tmap.items():
|
|
||||||
# XXX: SunOS does not retrieve UNIX sockets.
|
|
||||||
if kind == 'unix' and not HAS_CONNECTIONS_UNIX:
|
|
||||||
continue
|
|
||||||
families, types_ = groups
|
|
||||||
cons = psutil.net_connections(kind)
|
|
||||||
self.assertEqual(len(cons), len(set(cons)))
|
|
||||||
check(cons, families, types_)
|
|
||||||
|
|
||||||
self.assertRaises(ValueError, psutil.net_connections, kind='???')
|
|
||||||
|
|
||||||
@skip_on_access_denied()
|
|
||||||
def test_multi_socks(self):
|
|
||||||
with create_sockets() as socks:
|
|
||||||
cons = [x for x in psutil.net_connections(kind='all')
|
|
||||||
if x.pid == os.getpid()]
|
|
||||||
self.assertEqual(len(cons), len(socks))
|
|
||||||
|
|
||||||
@skip_on_access_denied()
|
|
||||||
# See: https://travis-ci.org/giampaolo/psutil/jobs/237566297
|
|
||||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on MACOS + TRAVIS")
|
|
||||||
def test_multi_sockets_procs(self):
|
|
||||||
# Creates multiple sub processes, each creating different
|
|
||||||
# sockets. For each process check that proc.connections()
|
|
||||||
# and net_connections() return the same results.
|
|
||||||
# This is done mainly to check whether net_connections()'s
|
|
||||||
# pid is properly set, see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1013
|
|
||||||
with create_sockets() as socks:
|
|
||||||
expected = len(socks)
|
|
||||||
pids = []
|
|
||||||
times = 10
|
|
||||||
for i in range(times):
|
|
||||||
fname = os.path.realpath(TESTFN) + str(i)
|
|
||||||
src = textwrap.dedent("""\
|
|
||||||
import time, os
|
|
||||||
from psutil.tests import create_sockets
|
|
||||||
with create_sockets():
|
|
||||||
with open('%s', 'w') as f:
|
|
||||||
f.write(str(os.getpid()))
|
|
||||||
time.sleep(60)
|
|
||||||
""" % fname)
|
|
||||||
sproc = pyrun(src)
|
|
||||||
pids.append(sproc.pid)
|
|
||||||
self.addCleanup(safe_rmpath, fname)
|
|
||||||
|
|
||||||
# sync
|
|
||||||
for i in range(times):
|
|
||||||
fname = TESTFN + str(i)
|
|
||||||
wait_for_file(fname)
|
|
||||||
|
|
||||||
syscons = [x for x in psutil.net_connections(kind='all') if x.pid
|
|
||||||
in pids]
|
|
||||||
for pid in pids:
|
|
||||||
self.assertEqual(len([x for x in syscons if x.pid == pid]),
|
|
||||||
expected)
|
|
||||||
p = psutil.Process(pid)
|
|
||||||
self.assertEqual(len(p.connections('all')), expected)
|
|
||||||
|
|
||||||
|
|
||||||
# =====================================================================
|
|
||||||
# --- Miscellaneous tests
|
|
||||||
# =====================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestMisc(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_connection_constants(self):
|
|
||||||
ints = []
|
|
||||||
strs = []
|
|
||||||
for name in dir(psutil):
|
|
||||||
if name.startswith('CONN_'):
|
|
||||||
num = getattr(psutil, name)
|
|
||||||
str_ = str(num)
|
|
||||||
assert str_.isupper(), str_
|
|
||||||
self.assertNotIn(str, strs)
|
|
||||||
self.assertNotIn(num, ints)
|
|
||||||
ints.append(num)
|
|
||||||
strs.append(str_)
|
|
||||||
if SUNOS:
|
|
||||||
psutil.CONN_IDLE
|
|
||||||
psutil.CONN_BOUND
|
|
||||||
if WINDOWS:
|
|
||||||
psutil.CONN_DELETE_TCB
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,642 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Contracts tests. These tests mainly check API sanity in terms of
|
|
||||||
returned types and APIs availability.
|
|
||||||
Some of these are duplicates of tests test_system.py and test_process.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import warnings
|
|
||||||
from contextlib import closing
|
|
||||||
|
|
||||||
from psutil import AIX
|
|
||||||
from psutil import BSD
|
|
||||||
from psutil import FREEBSD
|
|
||||||
from psutil import LINUX
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import NETBSD
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil._compat import long
|
|
||||||
from psutil.tests import bind_unix_socket
|
|
||||||
from psutil.tests import check_connection_ntuple
|
|
||||||
from psutil.tests import get_kernel_version
|
|
||||||
from psutil.tests import HAS_CONNECTIONS_UNIX
|
|
||||||
from psutil.tests import HAS_RLIMIT
|
|
||||||
from psutil.tests import HAS_SENSORS_FANS
|
|
||||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
|
||||||
from psutil.tests import is_namedtuple
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import safe_rmpath
|
|
||||||
from psutil.tests import skip_on_access_denied
|
|
||||||
from psutil.tests import TESTFN
|
|
||||||
from psutil.tests import unittest
|
|
||||||
from psutil.tests import unix_socket_path
|
|
||||||
from psutil.tests import VALID_PROC_STATUSES
|
|
||||||
from psutil.tests import warn
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# --- APIs availability
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestAvailability(unittest.TestCase):
|
|
||||||
"""Make sure code reflects what doc promises in terms of APIs
|
|
||||||
availability.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_cpu_affinity(self):
|
|
||||||
hasit = LINUX or WINDOWS or FREEBSD
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), hasit)
|
|
||||||
|
|
||||||
def test_win_service(self):
|
|
||||||
self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS)
|
|
||||||
self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS)
|
|
||||||
|
|
||||||
def test_PROCFS_PATH(self):
|
|
||||||
self.assertEqual(hasattr(psutil, "PROCFS_PATH"),
|
|
||||||
LINUX or SUNOS or AIX)
|
|
||||||
|
|
||||||
def test_win_priority(self):
|
|
||||||
ae = self.assertEqual
|
|
||||||
ae(hasattr(psutil, "ABOVE_NORMAL_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
ae(hasattr(psutil, "BELOW_NORMAL_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
ae(hasattr(psutil, "HIGH_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
ae(hasattr(psutil, "IDLE_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
ae(hasattr(psutil, "NORMAL_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
ae(hasattr(psutil, "REALTIME_PRIORITY_CLASS"), WINDOWS)
|
|
||||||
|
|
||||||
def test_linux_ioprio(self):
|
|
||||||
ae = self.assertEqual
|
|
||||||
ae(hasattr(psutil, "IOPRIO_CLASS_NONE"), LINUX)
|
|
||||||
ae(hasattr(psutil, "IOPRIO_CLASS_RT"), LINUX)
|
|
||||||
ae(hasattr(psutil, "IOPRIO_CLASS_BE"), LINUX)
|
|
||||||
ae(hasattr(psutil, "IOPRIO_CLASS_IDLE"), LINUX)
|
|
||||||
|
|
||||||
def test_linux_rlimit(self):
|
|
||||||
ae = self.assertEqual
|
|
||||||
hasit = LINUX and get_kernel_version() >= (2, 6, 36)
|
|
||||||
ae(hasattr(psutil.Process, "rlimit"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIM_INFINITY"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_AS"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_CORE"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_CPU"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_DATA"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_FSIZE"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_LOCKS"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_MEMLOCK"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_NOFILE"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_NPROC"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_RSS"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_STACK"), hasit)
|
|
||||||
|
|
||||||
hasit = LINUX and get_kernel_version() >= (3, 0)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_MSGQUEUE"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_NICE"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_RTPRIO"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_RTTIME"), hasit)
|
|
||||||
ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit)
|
|
||||||
|
|
||||||
def test_cpu_freq(self):
|
|
||||||
linux = (LINUX and
|
|
||||||
(os.path.exists("/sys/devices/system/cpu/cpufreq") or
|
|
||||||
os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq")))
|
|
||||||
self.assertEqual(hasattr(psutil, "cpu_freq"),
|
|
||||||
linux or MACOS or WINDOWS)
|
|
||||||
|
|
||||||
def test_sensors_temperatures(self):
|
|
||||||
self.assertEqual(
|
|
||||||
hasattr(psutil, "sensors_temperatures"), LINUX)
|
|
||||||
|
|
||||||
def test_sensors_fans(self):
|
|
||||||
self.assertEqual(hasattr(psutil, "sensors_fans"), LINUX)
|
|
||||||
|
|
||||||
def test_battery(self):
|
|
||||||
self.assertEqual(hasattr(psutil, "sensors_battery"),
|
|
||||||
LINUX or WINDOWS or FREEBSD or MACOS)
|
|
||||||
|
|
||||||
def test_proc_environ(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "environ"),
|
|
||||||
LINUX or MACOS or WINDOWS)
|
|
||||||
|
|
||||||
def test_proc_uids(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
|
|
||||||
|
|
||||||
def test_proc_gids(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "uids"), POSIX)
|
|
||||||
|
|
||||||
def test_proc_terminal(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "terminal"), POSIX)
|
|
||||||
|
|
||||||
def test_proc_ionice(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS)
|
|
||||||
|
|
||||||
def test_proc_rlimit(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX)
|
|
||||||
|
|
||||||
def test_proc_io_counters(self):
|
|
||||||
hasit = hasattr(psutil.Process, "io_counters")
|
|
||||||
self.assertEqual(hasit, False if MACOS or SUNOS else True)
|
|
||||||
|
|
||||||
def test_proc_num_fds(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "num_fds"), POSIX)
|
|
||||||
|
|
||||||
def test_proc_num_handles(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS)
|
|
||||||
|
|
||||||
def test_proc_cpu_affinity(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "cpu_affinity"),
|
|
||||||
LINUX or WINDOWS or FREEBSD)
|
|
||||||
|
|
||||||
def test_proc_cpu_num(self):
|
|
||||||
self.assertEqual(hasattr(psutil.Process, "cpu_num"),
|
|
||||||
LINUX or FREEBSD or SUNOS)
|
|
||||||
|
|
||||||
def test_proc_memory_maps(self):
|
|
||||||
hasit = hasattr(psutil.Process, "memory_maps")
|
|
||||||
self.assertEqual(hasit, False if OPENBSD or NETBSD or AIX else True)
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# --- Test deprecations
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestDeprecations(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_memory_info_ex(self):
|
|
||||||
with warnings.catch_warnings(record=True) as ws:
|
|
||||||
psutil.Process().memory_info_ex()
|
|
||||||
w = ws[0]
|
|
||||||
self.assertIsInstance(w.category(), FutureWarning)
|
|
||||||
self.assertIn("memory_info_ex() is deprecated", str(w.message))
|
|
||||||
self.assertIn("use memory_info() instead", str(w.message))
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# --- System API types
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestSystem(unittest.TestCase):
|
|
||||||
"""Check the return types of system related APIs.
|
|
||||||
Mainly we want to test we never return unicode on Python 2, see:
|
|
||||||
https://github.com/giampaolo/psutil/issues/1039
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.proc = psutil.Process()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
safe_rmpath(TESTFN)
|
|
||||||
|
|
||||||
def test_cpu_times(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
ret = psutil.cpu_times()
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for n in ret:
|
|
||||||
self.assertIsInstance(n, float)
|
|
||||||
self.assertGreaterEqual(n, 0)
|
|
||||||
|
|
||||||
def test_io_counters(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for k in psutil.disk_io_counters(perdisk=True):
|
|
||||||
self.assertIsInstance(k, str)
|
|
||||||
|
|
||||||
def test_disk_partitions(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for disk in psutil.disk_partitions():
|
|
||||||
self.assertIsInstance(disk.device, str)
|
|
||||||
self.assertIsInstance(disk.mountpoint, str)
|
|
||||||
self.assertIsInstance(disk.fstype, str)
|
|
||||||
self.assertIsInstance(disk.opts, str)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
|
||||||
@unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets")
|
|
||||||
@skip_on_access_denied(only_if=MACOS)
|
|
||||||
def test_net_connections(self):
|
|
||||||
with unix_socket_path() as name:
|
|
||||||
with closing(bind_unix_socket(name)):
|
|
||||||
cons = psutil.net_connections(kind='unix')
|
|
||||||
assert cons
|
|
||||||
for conn in cons:
|
|
||||||
self.assertIsInstance(conn.laddr, str)
|
|
||||||
|
|
||||||
def test_net_if_addrs(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for ifname, addrs in psutil.net_if_addrs().items():
|
|
||||||
self.assertIsInstance(ifname, str)
|
|
||||||
for addr in addrs:
|
|
||||||
self.assertIsInstance(addr.address, str)
|
|
||||||
self.assertIsInstance(addr.netmask, (str, type(None)))
|
|
||||||
self.assertIsInstance(addr.broadcast, (str, type(None)))
|
|
||||||
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for ifname, _ in psutil.net_if_stats().items():
|
|
||||||
self.assertIsInstance(ifname, str)
|
|
||||||
|
|
||||||
def test_net_io_counters(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for ifname, _ in psutil.net_io_counters(pernic=True).items():
|
|
||||||
self.assertIsInstance(ifname, str)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
|
|
||||||
def test_sensors_fans(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for name, units in psutil.sensors_fans().items():
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
for unit in units:
|
|
||||||
self.assertIsInstance(unit.label, str)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
|
|
||||||
def test_sensors_temperatures(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for name, units in psutil.sensors_temperatures().items():
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
for unit in units:
|
|
||||||
self.assertIsInstance(unit.label, str)
|
|
||||||
|
|
||||||
def test_users(self):
|
|
||||||
# Duplicate of test_system.py. Keep it anyway.
|
|
||||||
for user in psutil.users():
|
|
||||||
self.assertIsInstance(user.name, str)
|
|
||||||
self.assertIsInstance(user.terminal, (str, type(None)))
|
|
||||||
self.assertIsInstance(user.host, (str, type(None)))
|
|
||||||
self.assertIsInstance(user.pid, (int, type(None)))
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# --- Featch all processes test
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestFetchAllProcesses(unittest.TestCase):
|
|
||||||
"""Test which iterates over all running processes and performs
|
|
||||||
some sanity checks against Process API's returned values.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def test_fetch_all(self):
|
|
||||||
valid_procs = 0
|
|
||||||
excluded_names = set([
|
|
||||||
'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
|
|
||||||
'as_dict', 'parent', 'children', 'memory_info_ex', 'oneshot',
|
|
||||||
])
|
|
||||||
if LINUX and not HAS_RLIMIT:
|
|
||||||
excluded_names.add('rlimit')
|
|
||||||
attrs = []
|
|
||||||
for name in dir(psutil.Process):
|
|
||||||
if name.startswith("_"):
|
|
||||||
continue
|
|
||||||
if name in excluded_names:
|
|
||||||
continue
|
|
||||||
attrs.append(name)
|
|
||||||
|
|
||||||
default = object()
|
|
||||||
failures = []
|
|
||||||
for p in psutil.process_iter():
|
|
||||||
with p.oneshot():
|
|
||||||
for name in attrs:
|
|
||||||
ret = default
|
|
||||||
try:
|
|
||||||
args = ()
|
|
||||||
kwargs = {}
|
|
||||||
attr = getattr(p, name, None)
|
|
||||||
if attr is not None and callable(attr):
|
|
||||||
if name == 'rlimit':
|
|
||||||
args = (psutil.RLIMIT_NOFILE,)
|
|
||||||
elif name == 'memory_maps':
|
|
||||||
kwargs = {'grouped': False}
|
|
||||||
ret = attr(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
ret = attr
|
|
||||||
valid_procs += 1
|
|
||||||
except NotImplementedError:
|
|
||||||
msg = "%r was skipped because not implemented" % (
|
|
||||||
self.__class__.__name__ + '.test_' + name)
|
|
||||||
warn(msg)
|
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied) as err:
|
|
||||||
self.assertEqual(err.pid, p.pid)
|
|
||||||
if err.name:
|
|
||||||
# make sure exception's name attr is set
|
|
||||||
# with the actual process name
|
|
||||||
self.assertEqual(err.name, p.name())
|
|
||||||
assert str(err)
|
|
||||||
assert err.msg
|
|
||||||
except Exception as err:
|
|
||||||
s = '\n' + '=' * 70 + '\n'
|
|
||||||
s += "FAIL: test_%s (proc=%s" % (name, p)
|
|
||||||
if ret != default:
|
|
||||||
s += ", ret=%s)" % repr(ret)
|
|
||||||
s += ')\n'
|
|
||||||
s += '-' * 70
|
|
||||||
s += "\n%s" % traceback.format_exc()
|
|
||||||
s = "\n".join((" " * 4) + i for i in s.splitlines())
|
|
||||||
s += '\n'
|
|
||||||
failures.append(s)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if ret not in (0, 0.0, [], None, '', {}):
|
|
||||||
assert ret, ret
|
|
||||||
meth = getattr(self, name)
|
|
||||||
meth(ret, p)
|
|
||||||
|
|
||||||
if failures:
|
|
||||||
self.fail(''.join(failures))
|
|
||||||
|
|
||||||
# we should always have a non-empty list, not including PID 0 etc.
|
|
||||||
# special cases.
|
|
||||||
assert valid_procs
|
|
||||||
|
|
||||||
def cmdline(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, list)
|
|
||||||
for part in ret:
|
|
||||||
self.assertIsInstance(part, str)
|
|
||||||
|
|
||||||
def exe(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, (str, type(None)))
|
|
||||||
if not ret:
|
|
||||||
self.assertEqual(ret, '')
|
|
||||||
else:
|
|
||||||
assert os.path.isabs(ret), ret
|
|
||||||
# Note: os.stat() may return False even if the file is there
|
|
||||||
# hence we skip the test, see:
|
|
||||||
# http://stackoverflow.com/questions/3112546/os-path-exists-lies
|
|
||||||
if POSIX and os.path.isfile(ret):
|
|
||||||
if hasattr(os, 'access') and hasattr(os, "X_OK"):
|
|
||||||
# XXX may fail on MACOS
|
|
||||||
assert os.access(ret, os.X_OK)
|
|
||||||
|
|
||||||
def pid(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
|
|
||||||
def ppid(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, (int, long))
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
|
|
||||||
def name(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, str)
|
|
||||||
# on AIX, "<exiting>" processes don't have names
|
|
||||||
if not AIX:
|
|
||||||
assert ret
|
|
||||||
|
|
||||||
def create_time(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, float)
|
|
||||||
try:
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
except AssertionError:
|
|
||||||
# XXX
|
|
||||||
if OPENBSD and proc.status() == psutil.STATUS_ZOMBIE:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
# this can't be taken for granted on all platforms
|
|
||||||
# self.assertGreaterEqual(ret, psutil.boot_time())
|
|
||||||
# make sure returned value can be pretty printed
|
|
||||||
# with strftime
|
|
||||||
time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret))
|
|
||||||
|
|
||||||
def uids(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for uid in ret:
|
|
||||||
self.assertIsInstance(uid, int)
|
|
||||||
self.assertGreaterEqual(uid, 0)
|
|
||||||
|
|
||||||
def gids(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
# note: testing all gids as above seems not to be reliable for
|
|
||||||
# gid == 30 (nodoby); not sure why.
|
|
||||||
for gid in ret:
|
|
||||||
self.assertIsInstance(gid, int)
|
|
||||||
if not MACOS and not NETBSD:
|
|
||||||
self.assertGreaterEqual(gid, 0)
|
|
||||||
|
|
||||||
def username(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, str)
|
|
||||||
assert ret
|
|
||||||
|
|
||||||
def status(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, str)
|
|
||||||
assert ret
|
|
||||||
self.assertNotEqual(ret, '?') # XXX
|
|
||||||
self.assertIn(ret, VALID_PROC_STATUSES)
|
|
||||||
|
|
||||||
def io_counters(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for field in ret:
|
|
||||||
self.assertIsInstance(field, (int, long))
|
|
||||||
if field != -1:
|
|
||||||
self.assertGreaterEqual(field, 0)
|
|
||||||
|
|
||||||
def ionice(self, ret, proc):
|
|
||||||
if POSIX:
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for field in ret:
|
|
||||||
self.assertIsInstance(field, int)
|
|
||||||
if LINUX:
|
|
||||||
self.assertGreaterEqual(ret.ioclass, 0)
|
|
||||||
self.assertGreaterEqual(ret.value, 0)
|
|
||||||
else:
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
self.assertIn(ret, (0, 1, 2))
|
|
||||||
|
|
||||||
def num_threads(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
self.assertGreaterEqual(ret, 1)
|
|
||||||
|
|
||||||
def threads(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, list)
|
|
||||||
for t in ret:
|
|
||||||
assert is_namedtuple(t)
|
|
||||||
self.assertGreaterEqual(t.id, 0)
|
|
||||||
self.assertGreaterEqual(t.user_time, 0)
|
|
||||||
self.assertGreaterEqual(t.system_time, 0)
|
|
||||||
for field in t:
|
|
||||||
self.assertIsInstance(field, (int, float))
|
|
||||||
|
|
||||||
def cpu_times(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for n in ret:
|
|
||||||
self.assertIsInstance(n, float)
|
|
||||||
self.assertGreaterEqual(n, 0)
|
|
||||||
# TODO: check ntuple fields
|
|
||||||
|
|
||||||
def cpu_percent(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, float)
|
|
||||||
assert 0.0 <= ret <= 100.0, ret
|
|
||||||
|
|
||||||
def cpu_num(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
if FREEBSD and ret == -1:
|
|
||||||
return
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
if psutil.cpu_count() == 1:
|
|
||||||
self.assertEqual(ret, 0)
|
|
||||||
self.assertIn(ret, list(range(psutil.cpu_count())))
|
|
||||||
|
|
||||||
def memory_info(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for value in ret:
|
|
||||||
self.assertIsInstance(value, (int, long))
|
|
||||||
self.assertGreaterEqual(value, 0)
|
|
||||||
if POSIX and not AIX and ret.vms != 0:
|
|
||||||
# VMS is always supposed to be the highest
|
|
||||||
for name in ret._fields:
|
|
||||||
if name != 'vms':
|
|
||||||
value = getattr(ret, name)
|
|
||||||
self.assertGreater(ret.vms, value, msg=ret)
|
|
||||||
elif WINDOWS:
|
|
||||||
self.assertGreaterEqual(ret.peak_wset, ret.wset)
|
|
||||||
self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool)
|
|
||||||
self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool)
|
|
||||||
self.assertGreaterEqual(ret.peak_pagefile, ret.pagefile)
|
|
||||||
|
|
||||||
def memory_full_info(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
total = psutil.virtual_memory().total
|
|
||||||
for name in ret._fields:
|
|
||||||
value = getattr(ret, name)
|
|
||||||
self.assertIsInstance(value, (int, long))
|
|
||||||
self.assertGreaterEqual(value, 0, msg=(name, value))
|
|
||||||
if LINUX and name in ('vms', 'data'):
|
|
||||||
# On Linux there are processes (e.g. 'goa-daemon') whose
|
|
||||||
# VMS is incredibly high for some reason.
|
|
||||||
continue
|
|
||||||
self.assertLessEqual(value, total, msg=(name, value, total))
|
|
||||||
|
|
||||||
if LINUX:
|
|
||||||
self.assertGreaterEqual(ret.pss, ret.uss)
|
|
||||||
|
|
||||||
def open_files(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, list)
|
|
||||||
for f in ret:
|
|
||||||
self.assertIsInstance(f.fd, int)
|
|
||||||
self.assertIsInstance(f.path, str)
|
|
||||||
if WINDOWS:
|
|
||||||
self.assertEqual(f.fd, -1)
|
|
||||||
elif LINUX:
|
|
||||||
self.assertIsInstance(f.position, int)
|
|
||||||
self.assertIsInstance(f.mode, str)
|
|
||||||
self.assertIsInstance(f.flags, int)
|
|
||||||
self.assertGreaterEqual(f.position, 0)
|
|
||||||
self.assertIn(f.mode, ('r', 'w', 'a', 'r+', 'a+'))
|
|
||||||
self.assertGreater(f.flags, 0)
|
|
||||||
elif BSD and not f.path:
|
|
||||||
# XXX see: https://github.com/giampaolo/psutil/issues/595
|
|
||||||
continue
|
|
||||||
assert os.path.isabs(f.path), f
|
|
||||||
assert os.path.isfile(f.path), f
|
|
||||||
|
|
||||||
def num_fds(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
|
|
||||||
def connections(self, ret, proc):
|
|
||||||
self.assertEqual(len(ret), len(set(ret)))
|
|
||||||
for conn in ret:
|
|
||||||
check_connection_ntuple(conn)
|
|
||||||
|
|
||||||
def cwd(self, ret, proc):
|
|
||||||
if ret: # 'ret' can be None or empty
|
|
||||||
self.assertIsInstance(ret, str)
|
|
||||||
assert os.path.isabs(ret), ret
|
|
||||||
try:
|
|
||||||
st = os.stat(ret)
|
|
||||||
except OSError as err:
|
|
||||||
if WINDOWS and err.errno in \
|
|
||||||
psutil._psplatform.ACCESS_DENIED_SET:
|
|
||||||
pass
|
|
||||||
# directory has been removed in mean time
|
|
||||||
elif err.errno != errno.ENOENT:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
assert stat.S_ISDIR(st.st_mode)
|
|
||||||
|
|
||||||
def memory_percent(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, float)
|
|
||||||
assert 0 <= ret <= 100, ret
|
|
||||||
|
|
||||||
def is_running(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, bool)
|
|
||||||
|
|
||||||
def cpu_affinity(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, list)
|
|
||||||
assert ret != [], ret
|
|
||||||
cpus = range(psutil.cpu_count())
|
|
||||||
for n in ret:
|
|
||||||
self.assertIsInstance(n, int)
|
|
||||||
self.assertIn(n, cpus)
|
|
||||||
|
|
||||||
def terminal(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, (str, type(None)))
|
|
||||||
if ret is not None:
|
|
||||||
assert os.path.isabs(ret), ret
|
|
||||||
assert os.path.exists(ret), ret
|
|
||||||
|
|
||||||
def memory_maps(self, ret, proc):
|
|
||||||
for nt in ret:
|
|
||||||
self.assertIsInstance(nt.addr, str)
|
|
||||||
self.assertIsInstance(nt.perms, str)
|
|
||||||
self.assertIsInstance(nt.path, str)
|
|
||||||
for fname in nt._fields:
|
|
||||||
value = getattr(nt, fname)
|
|
||||||
if fname == 'path':
|
|
||||||
if not value.startswith('['):
|
|
||||||
assert os.path.isabs(nt.path), nt.path
|
|
||||||
# commented as on Linux we might get
|
|
||||||
# '/foo/bar (deleted)'
|
|
||||||
# assert os.path.exists(nt.path), nt.path
|
|
||||||
elif fname in ('addr', 'perms'):
|
|
||||||
assert value
|
|
||||||
else:
|
|
||||||
self.assertIsInstance(value, (int, long))
|
|
||||||
self.assertGreaterEqual(value, 0)
|
|
||||||
|
|
||||||
def num_handles(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
self.assertGreaterEqual(ret, 0)
|
|
||||||
|
|
||||||
def nice(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, int)
|
|
||||||
if POSIX:
|
|
||||||
assert -20 <= ret <= 20, ret
|
|
||||||
else:
|
|
||||||
priorities = [getattr(psutil, x) for x in dir(psutil)
|
|
||||||
if x.endswith('_PRIORITY_CLASS')]
|
|
||||||
self.assertIn(ret, priorities)
|
|
||||||
|
|
||||||
def num_ctx_switches(self, ret, proc):
|
|
||||||
assert is_namedtuple(ret)
|
|
||||||
for value in ret:
|
|
||||||
self.assertIsInstance(value, (int, long))
|
|
||||||
self.assertGreaterEqual(value, 0)
|
|
||||||
|
|
||||||
def rlimit(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, tuple)
|
|
||||||
self.assertEqual(len(ret), 2)
|
|
||||||
self.assertGreaterEqual(ret[0], -1)
|
|
||||||
self.assertGreaterEqual(ret[1], -1)
|
|
||||||
|
|
||||||
def environ(self, ret, proc):
|
|
||||||
self.assertIsInstance(ret, dict)
|
|
||||||
for k, v in ret.items():
|
|
||||||
self.assertIsInstance(k, str)
|
|
||||||
self.assertIsInstance(v, str)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,599 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Tests for detecting function memory leaks (typically the ones
|
|
||||||
implemented in C). It does so by calling a function many times and
|
|
||||||
checking whether process memory usage keeps increasing between
|
|
||||||
calls or over time.
|
|
||||||
Note that this may produce false positives (especially on Windows
|
|
||||||
for some reason).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import errno
|
|
||||||
import functools
|
|
||||||
import gc
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
import psutil._common
|
|
||||||
from psutil import LINUX
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil._compat import xrange
|
|
||||||
from psutil.tests import create_sockets
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_CPU_AFFINITY
|
|
||||||
from psutil.tests import HAS_CPU_FREQ
|
|
||||||
from psutil.tests import HAS_ENVIRON
|
|
||||||
from psutil.tests import HAS_IONICE
|
|
||||||
from psutil.tests import HAS_MEMORY_MAPS
|
|
||||||
from psutil.tests import HAS_PROC_CPU_NUM
|
|
||||||
from psutil.tests import HAS_PROC_IO_COUNTERS
|
|
||||||
from psutil.tests import HAS_RLIMIT
|
|
||||||
from psutil.tests import HAS_SENSORS_BATTERY
|
|
||||||
from psutil.tests import HAS_SENSORS_FANS
|
|
||||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import safe_rmpath
|
|
||||||
from psutil.tests import skip_on_access_denied
|
|
||||||
from psutil.tests import TESTFN
|
|
||||||
from psutil.tests import TRAVIS
|
|
||||||
from psutil.tests import unittest
|
|
||||||
|
|
||||||
|
|
||||||
LOOPS = 1000
|
|
||||||
MEMORY_TOLERANCE = 4096
|
|
||||||
RETRY_FOR = 3
|
|
||||||
|
|
||||||
SKIP_PYTHON_IMPL = True if TRAVIS else False
|
|
||||||
cext = psutil._psplatform.cext
|
|
||||||
thisproc = psutil.Process()
|
|
||||||
SKIP_PYTHON_IMPL = True if TRAVIS else False
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# utils
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
def skip_if_linux():
|
|
||||||
return unittest.skipIf(LINUX and SKIP_PYTHON_IMPL,
|
|
||||||
"worthless on LINUX (pure python)")
|
|
||||||
|
|
||||||
|
|
||||||
def bytes2human(n):
|
|
||||||
"""
|
|
||||||
http://code.activestate.com/recipes/578019
|
|
||||||
>>> bytes2human(10000)
|
|
||||||
'9.8K'
|
|
||||||
>>> bytes2human(100001221)
|
|
||||||
'95.4M'
|
|
||||||
"""
|
|
||||||
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
|
||||||
prefix = {}
|
|
||||||
for i, s in enumerate(symbols):
|
|
||||||
prefix[s] = 1 << (i + 1) * 10
|
|
||||||
for s in reversed(symbols):
|
|
||||||
if n >= prefix[s]:
|
|
||||||
value = float(n) / prefix[s]
|
|
||||||
return '%.2f%s' % (value, s)
|
|
||||||
return "%sB" % n
|
|
||||||
|
|
||||||
|
|
||||||
class TestMemLeak(unittest.TestCase):
|
|
||||||
"""Base framework class which calls a function many times and
|
|
||||||
produces a failure if process memory usage keeps increasing
|
|
||||||
between calls or over time.
|
|
||||||
"""
|
|
||||||
tolerance = MEMORY_TOLERANCE
|
|
||||||
loops = LOOPS
|
|
||||||
retry_for = RETRY_FOR
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
def execute(self, fun, *args, **kwargs):
|
|
||||||
"""Test a callable."""
|
|
||||||
def call_many_times():
|
|
||||||
for x in xrange(loops):
|
|
||||||
self._call(fun, *args, **kwargs)
|
|
||||||
del x
|
|
||||||
gc.collect()
|
|
||||||
|
|
||||||
tolerance = kwargs.pop('tolerance_', None) or self.tolerance
|
|
||||||
loops = kwargs.pop('loops_', None) or self.loops
|
|
||||||
retry_for = kwargs.pop('retry_for_', None) or self.retry_for
|
|
||||||
|
|
||||||
# warm up
|
|
||||||
for x in range(10):
|
|
||||||
self._call(fun, *args, **kwargs)
|
|
||||||
self.assertEqual(gc.garbage, [])
|
|
||||||
self.assertEqual(threading.active_count(), 1)
|
|
||||||
self.assertEqual(thisproc.children(), [])
|
|
||||||
|
|
||||||
# Get 2 distinct memory samples, before and after having
|
|
||||||
# called fun repeadetly.
|
|
||||||
# step 1
|
|
||||||
call_many_times()
|
|
||||||
mem1 = self._get_mem()
|
|
||||||
# step 2
|
|
||||||
call_many_times()
|
|
||||||
mem2 = self._get_mem()
|
|
||||||
|
|
||||||
diff1 = mem2 - mem1
|
|
||||||
if diff1 > tolerance:
|
|
||||||
# This doesn't necessarily mean we have a leak yet.
|
|
||||||
# At this point we assume that after having called the
|
|
||||||
# function so many times the memory usage is stabilized
|
|
||||||
# and if there are no leaks it should not increase
|
|
||||||
# anymore.
|
|
||||||
# Let's keep calling fun for 3 more seconds and fail if
|
|
||||||
# we notice any difference.
|
|
||||||
ncalls = 0
|
|
||||||
stop_at = time.time() + retry_for
|
|
||||||
while time.time() <= stop_at:
|
|
||||||
self._call(fun, *args, **kwargs)
|
|
||||||
ncalls += 1
|
|
||||||
|
|
||||||
del stop_at
|
|
||||||
gc.collect()
|
|
||||||
mem3 = self._get_mem()
|
|
||||||
diff2 = mem3 - mem2
|
|
||||||
|
|
||||||
if mem3 > mem2:
|
|
||||||
# failure
|
|
||||||
extra_proc_mem = bytes2human(diff1 + diff2)
|
|
||||||
print("exta proc mem: %s" % extra_proc_mem, file=sys.stderr)
|
|
||||||
msg = "+%s after %s calls, +%s after another %s calls, "
|
|
||||||
msg += "+%s extra proc mem"
|
|
||||||
msg = msg % (
|
|
||||||
bytes2human(diff1), loops, bytes2human(diff2), ncalls,
|
|
||||||
extra_proc_mem)
|
|
||||||
self.fail(msg)
|
|
||||||
|
|
||||||
def execute_w_exc(self, exc, fun, *args, **kwargs):
|
|
||||||
"""Convenience function which tests a callable raising
|
|
||||||
an exception.
|
|
||||||
"""
|
|
||||||
def call():
|
|
||||||
self.assertRaises(exc, fun, *args, **kwargs)
|
|
||||||
|
|
||||||
self.execute(call)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_mem():
|
|
||||||
# By using USS memory it seems it's less likely to bump
|
|
||||||
# into false positives.
|
|
||||||
if LINUX or WINDOWS or MACOS:
|
|
||||||
return thisproc.memory_full_info().uss
|
|
||||||
else:
|
|
||||||
return thisproc.memory_info().rss
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _call(fun, *args, **kwargs):
|
|
||||||
fun(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# Process class
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestProcessObjectLeaks(TestMemLeak):
|
|
||||||
"""Test leaks of Process class methods."""
|
|
||||||
|
|
||||||
proc = thisproc
|
|
||||||
|
|
||||||
def test_coverage(self):
|
|
||||||
skip = set((
|
|
||||||
"pid", "as_dict", "children", "cpu_affinity", "cpu_percent",
|
|
||||||
"ionice", "is_running", "kill", "memory_info_ex", "memory_percent",
|
|
||||||
"nice", "oneshot", "parent", "rlimit", "send_signal", "suspend",
|
|
||||||
"terminate", "wait"))
|
|
||||||
for name in dir(psutil.Process):
|
|
||||||
if name.startswith('_'):
|
|
||||||
continue
|
|
||||||
if name in skip:
|
|
||||||
continue
|
|
||||||
self.assertTrue(hasattr(self, "test_" + name), msg=name)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_name(self):
|
|
||||||
self.execute(self.proc.name)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cmdline(self):
|
|
||||||
self.execute(self.proc.cmdline)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_exe(self):
|
|
||||||
self.execute(self.proc.exe)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_ppid(self):
|
|
||||||
self.execute(self.proc.ppid)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_uids(self):
|
|
||||||
self.execute(self.proc.uids)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_gids(self):
|
|
||||||
self.execute(self.proc.gids)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_status(self):
|
|
||||||
self.execute(self.proc.status)
|
|
||||||
|
|
||||||
def test_nice_get(self):
|
|
||||||
self.execute(self.proc.nice)
|
|
||||||
|
|
||||||
def test_nice_set(self):
|
|
||||||
niceness = thisproc.nice()
|
|
||||||
self.execute(self.proc.nice, niceness)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_IONICE, "not supported")
|
|
||||||
def test_ionice_get(self):
|
|
||||||
self.execute(self.proc.ionice)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_IONICE, "not supported")
|
|
||||||
def test_ionice_set(self):
|
|
||||||
if WINDOWS:
|
|
||||||
value = thisproc.ionice()
|
|
||||||
self.execute(self.proc.ionice, value)
|
|
||||||
else:
|
|
||||||
self.execute(self.proc.ionice, psutil.IOPRIO_CLASS_NONE)
|
|
||||||
fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0)
|
|
||||||
self.execute_w_exc(OSError, fun)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_PROC_IO_COUNTERS, "not supported")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_io_counters(self):
|
|
||||||
self.execute(self.proc.io_counters)
|
|
||||||
|
|
||||||
@unittest.skipIf(POSIX, "worthless on POSIX")
|
|
||||||
def test_username(self):
|
|
||||||
self.execute(self.proc.username)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_create_time(self):
|
|
||||||
self.execute(self.proc.create_time)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@skip_on_access_denied(only_if=OPENBSD)
|
|
||||||
def test_num_threads(self):
|
|
||||||
self.execute(self.proc.num_threads)
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
def test_num_handles(self):
|
|
||||||
self.execute(self.proc.num_handles)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_num_fds(self):
|
|
||||||
self.execute(self.proc.num_fds)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_num_ctx_switches(self):
|
|
||||||
self.execute(self.proc.num_ctx_switches)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@skip_on_access_denied(only_if=OPENBSD)
|
|
||||||
def test_threads(self):
|
|
||||||
self.execute(self.proc.threads)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cpu_times(self):
|
|
||||||
self.execute(self.proc.cpu_times)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@unittest.skipIf(not HAS_PROC_CPU_NUM, "not supported")
|
|
||||||
def test_cpu_num(self):
|
|
||||||
self.execute(self.proc.cpu_num)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_memory_info(self):
|
|
||||||
self.execute(self.proc.memory_info)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_memory_full_info(self):
|
|
||||||
self.execute(self.proc.memory_full_info)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_terminal(self):
|
|
||||||
self.execute(self.proc.terminal)
|
|
||||||
|
|
||||||
@unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
|
|
||||||
"worthless on POSIX (pure python)")
|
|
||||||
def test_resume(self):
|
|
||||||
self.execute(self.proc.resume)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cwd(self):
|
|
||||||
self.execute(self.proc.cwd)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_CPU_AFFINITY, "not supported")
|
|
||||||
def test_cpu_affinity_get(self):
|
|
||||||
self.execute(self.proc.cpu_affinity)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_CPU_AFFINITY, "not supported")
|
|
||||||
def test_cpu_affinity_set(self):
|
|
||||||
affinity = thisproc.cpu_affinity()
|
|
||||||
self.execute(self.proc.cpu_affinity, affinity)
|
|
||||||
if not TRAVIS:
|
|
||||||
self.execute_w_exc(ValueError, self.proc.cpu_affinity, [-1])
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_open_files(self):
|
|
||||||
safe_rmpath(TESTFN) # needed after UNIX socket test has run
|
|
||||||
with open(TESTFN, 'w'):
|
|
||||||
self.execute(self.proc.open_files)
|
|
||||||
|
|
||||||
# MACOS implementation is unbelievably slow
|
|
||||||
@unittest.skipIf(MACOS, "too slow on MACOS")
|
|
||||||
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_memory_maps(self):
|
|
||||||
self.execute(self.proc.memory_maps)
|
|
||||||
|
|
||||||
@unittest.skipIf(not LINUX, "LINUX only")
|
|
||||||
@unittest.skipIf(not HAS_RLIMIT, "not supported")
|
|
||||||
def test_rlimit_get(self):
|
|
||||||
self.execute(self.proc.rlimit, psutil.RLIMIT_NOFILE)
|
|
||||||
|
|
||||||
@unittest.skipIf(not LINUX, "LINUX only")
|
|
||||||
@unittest.skipIf(not HAS_RLIMIT, "not supported")
|
|
||||||
def test_rlimit_set(self):
|
|
||||||
limit = thisproc.rlimit(psutil.RLIMIT_NOFILE)
|
|
||||||
self.execute(self.proc.rlimit, psutil.RLIMIT_NOFILE, limit)
|
|
||||||
self.execute_w_exc(OSError, self.proc.rlimit, -1)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
# Windows implementation is based on a single system-wide
|
|
||||||
# function (tested later).
|
|
||||||
@unittest.skipIf(WINDOWS, "worthless on WINDOWS")
|
|
||||||
def test_connections(self):
|
|
||||||
# TODO: UNIX sockets are temporarily implemented by parsing
|
|
||||||
# 'pfiles' cmd output; we don't want that part of the code to
|
|
||||||
# be executed.
|
|
||||||
with create_sockets():
|
|
||||||
kind = 'inet' if SUNOS else 'all'
|
|
||||||
self.execute(self.proc.connections, kind)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_ENVIRON, "not supported")
|
|
||||||
def test_environ(self):
|
|
||||||
self.execute(self.proc.environ)
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
def test_proc_info(self):
|
|
||||||
self.execute(cext.proc_info, os.getpid())
|
|
||||||
|
|
||||||
|
|
||||||
class TestTerminatedProcessLeaks(TestProcessObjectLeaks):
|
|
||||||
"""Repeat the tests above looking for leaks occurring when dealing
|
|
||||||
with terminated processes raising NoSuchProcess exception.
|
|
||||||
The C functions are still invoked but will follow different code
|
|
||||||
paths. We'll check those code paths.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super(TestTerminatedProcessLeaks, cls).setUpClass()
|
|
||||||
p = get_test_subprocess()
|
|
||||||
cls.proc = psutil.Process(p.pid)
|
|
||||||
cls.proc.kill()
|
|
||||||
cls.proc.wait()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
super(TestTerminatedProcessLeaks, cls).tearDownClass()
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def _call(self, fun, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
fun(*args, **kwargs)
|
|
||||||
except psutil.NoSuchProcess:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if WINDOWS:
|
|
||||||
|
|
||||||
def test_kill(self):
|
|
||||||
self.execute(self.proc.kill)
|
|
||||||
|
|
||||||
def test_terminate(self):
|
|
||||||
self.execute(self.proc.terminate)
|
|
||||||
|
|
||||||
def test_suspend(self):
|
|
||||||
self.execute(self.proc.suspend)
|
|
||||||
|
|
||||||
def test_resume(self):
|
|
||||||
self.execute(self.proc.resume)
|
|
||||||
|
|
||||||
def test_wait(self):
|
|
||||||
self.execute(self.proc.wait)
|
|
||||||
|
|
||||||
def test_proc_info(self):
|
|
||||||
# test dual implementation
|
|
||||||
def call():
|
|
||||||
try:
|
|
||||||
return cext.proc_info(self.proc.pid)
|
|
||||||
except OSError as err:
|
|
||||||
if err.errno != errno.ESRCH:
|
|
||||||
raise
|
|
||||||
|
|
||||||
self.execute(call)
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# system APIs
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestModuleFunctionsLeaks(TestMemLeak):
|
|
||||||
"""Test leaks of psutil module functions."""
|
|
||||||
|
|
||||||
def test_coverage(self):
|
|
||||||
skip = set((
|
|
||||||
"version_info", "__version__", "process_iter", "wait_procs",
|
|
||||||
"cpu_percent", "cpu_times_percent", "cpu_count"))
|
|
||||||
for name in psutil.__all__:
|
|
||||||
if not name.islower():
|
|
||||||
continue
|
|
||||||
if name in skip:
|
|
||||||
continue
|
|
||||||
self.assertTrue(hasattr(self, "test_" + name), msg=name)
|
|
||||||
|
|
||||||
# --- cpu
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cpu_count_logical(self):
|
|
||||||
self.execute(psutil.cpu_count, logical=True)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cpu_count_physical(self):
|
|
||||||
self.execute(psutil.cpu_count, logical=False)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_cpu_times(self):
|
|
||||||
self.execute(psutil.cpu_times)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_per_cpu_times(self):
|
|
||||||
self.execute(psutil.cpu_times, percpu=True)
|
|
||||||
|
|
||||||
def test_cpu_stats(self):
|
|
||||||
self.execute(psutil.cpu_stats)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@unittest.skipIf(not HAS_CPU_FREQ, "not supported")
|
|
||||||
def test_cpu_freq(self):
|
|
||||||
self.execute(psutil.cpu_freq)
|
|
||||||
|
|
||||||
# --- mem
|
|
||||||
|
|
||||||
def test_virtual_memory(self):
|
|
||||||
self.execute(psutil.virtual_memory)
|
|
||||||
|
|
||||||
# TODO: remove this skip when this gets fixed
|
|
||||||
@unittest.skipIf(SUNOS,
|
|
||||||
"worthless on SUNOS (uses a subprocess)")
|
|
||||||
def test_swap_memory(self):
|
|
||||||
self.execute(psutil.swap_memory)
|
|
||||||
|
|
||||||
@unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
|
|
||||||
"worthless on POSIX (pure python)")
|
|
||||||
def test_pid_exists(self):
|
|
||||||
self.execute(psutil.pid_exists, os.getpid())
|
|
||||||
|
|
||||||
# --- disk
|
|
||||||
|
|
||||||
@unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
|
|
||||||
"worthless on POSIX (pure python)")
|
|
||||||
def test_disk_usage(self):
|
|
||||||
self.execute(psutil.disk_usage, '.')
|
|
||||||
|
|
||||||
def test_disk_partitions(self):
|
|
||||||
self.execute(psutil.disk_partitions)
|
|
||||||
|
|
||||||
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
|
|
||||||
'/proc/diskstats not available on this Linux version')
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_disk_io_counters(self):
|
|
||||||
self.execute(psutil.disk_io_counters, nowrap=False)
|
|
||||||
|
|
||||||
# --- proc
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_pids(self):
|
|
||||||
self.execute(psutil.pids)
|
|
||||||
|
|
||||||
# --- net
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_net_io_counters(self):
|
|
||||||
self.execute(psutil.net_io_counters, nowrap=False)
|
|
||||||
|
|
||||||
@unittest.skipIf(LINUX,
|
|
||||||
"worthless on Linux (pure python)")
|
|
||||||
@unittest.skipIf(MACOS and os.getuid() != 0, "need root access")
|
|
||||||
def test_net_connections(self):
|
|
||||||
with create_sockets():
|
|
||||||
self.execute(psutil.net_connections)
|
|
||||||
|
|
||||||
def test_net_if_addrs(self):
|
|
||||||
# Note: verified that on Windows this was a false positive.
|
|
||||||
self.execute(psutil.net_if_addrs,
|
|
||||||
tolerance_=80 * 1024 if WINDOWS else None)
|
|
||||||
|
|
||||||
@unittest.skipIf(TRAVIS, "EPERM on travis")
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
self.execute(psutil.net_if_stats)
|
|
||||||
|
|
||||||
# --- sensors
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported")
|
|
||||||
def test_sensors_battery(self):
|
|
||||||
self.execute(psutil.sensors_battery)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
|
|
||||||
def test_sensors_temperatures(self):
|
|
||||||
self.execute(psutil.sensors_temperatures)
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
|
|
||||||
def test_sensors_fans(self):
|
|
||||||
self.execute(psutil.sensors_fans)
|
|
||||||
|
|
||||||
# --- others
|
|
||||||
|
|
||||||
@skip_if_linux()
|
|
||||||
def test_boot_time(self):
|
|
||||||
self.execute(psutil.boot_time)
|
|
||||||
|
|
||||||
# XXX - on Windows this produces a false positive
|
|
||||||
@unittest.skipIf(WINDOWS, "XXX produces a false positive on Windows")
|
|
||||||
def test_users(self):
|
|
||||||
self.execute(psutil.users)
|
|
||||||
|
|
||||||
if WINDOWS:
|
|
||||||
|
|
||||||
# --- win services
|
|
||||||
|
|
||||||
def test_win_service_iter(self):
|
|
||||||
self.execute(cext.winservice_enumerate)
|
|
||||||
|
|
||||||
def test_win_service_get(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_win_service_get_config(self):
|
|
||||||
name = next(psutil.win_service_iter()).name()
|
|
||||||
self.execute(cext.winservice_query_config, name)
|
|
||||||
|
|
||||||
def test_win_service_get_status(self):
|
|
||||||
name = next(psutil.win_service_iter()).name()
|
|
||||||
self.execute(cext.winservice_query_status, name)
|
|
||||||
|
|
||||||
def test_win_service_get_description(self):
|
|
||||||
name = next(psutil.win_service_iter()).name()
|
|
||||||
self.execute(cext.winservice_query_descr, name)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,303 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""MACOS specific tests."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil.tests import create_zombie_proc
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_BATTERY
|
|
||||||
from psutil.tests import MEMORY_TOLERANCE
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import retry_before_failing
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import unittest
|
|
||||||
|
|
||||||
|
|
||||||
PAGESIZE = os.sysconf("SC_PAGE_SIZE") if MACOS else None
|
|
||||||
|
|
||||||
|
|
||||||
def sysctl(cmdline):
|
|
||||||
"""Expects a sysctl command with an argument and parse the result
|
|
||||||
returning only the value of interest.
|
|
||||||
"""
|
|
||||||
out = sh(cmdline)
|
|
||||||
result = out.split()[1]
|
|
||||||
try:
|
|
||||||
return int(result)
|
|
||||||
except ValueError:
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def vm_stat(field):
|
|
||||||
"""Wrapper around 'vm_stat' cmdline utility."""
|
|
||||||
out = sh('vm_stat')
|
|
||||||
for line in out.split('\n'):
|
|
||||||
if field in line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("line not found")
|
|
||||||
return int(re.search(r'\d+', line).group(0)) * PAGESIZE
|
|
||||||
|
|
||||||
|
|
||||||
# http://code.activestate.com/recipes/578019/
|
|
||||||
def human2bytes(s):
|
|
||||||
SYMBOLS = {
|
|
||||||
'customary': ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'),
|
|
||||||
}
|
|
||||||
init = s
|
|
||||||
num = ""
|
|
||||||
while s and s[0:1].isdigit() or s[0:1] == '.':
|
|
||||||
num += s[0]
|
|
||||||
s = s[1:]
|
|
||||||
num = float(num)
|
|
||||||
letter = s.strip()
|
|
||||||
for name, sset in SYMBOLS.items():
|
|
||||||
if letter in sset:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if letter == 'k':
|
|
||||||
sset = SYMBOLS['customary']
|
|
||||||
letter = letter.upper()
|
|
||||||
else:
|
|
||||||
raise ValueError("can't interpret %r" % init)
|
|
||||||
prefix = {sset[0]: 1}
|
|
||||||
for i, s in enumerate(sset[1:]):
|
|
||||||
prefix[s] = 1 << (i + 1) * 10
|
|
||||||
return int(num * prefix[letter])
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not MACOS, "MACOS only")
|
|
||||||
class TestProcess(unittest.TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_process_create_time(self):
|
|
||||||
output = sh("ps -o lstart -p %s" % self.pid)
|
|
||||||
start_ps = output.replace('STARTED', '').strip()
|
|
||||||
hhmmss = start_ps.split(' ')[-2]
|
|
||||||
year = start_ps.split(' ')[-1]
|
|
||||||
start_psutil = psutil.Process(self.pid).create_time()
|
|
||||||
self.assertEqual(
|
|
||||||
hhmmss,
|
|
||||||
time.strftime("%H:%M:%S", time.localtime(start_psutil)))
|
|
||||||
self.assertEqual(
|
|
||||||
year,
|
|
||||||
time.strftime("%Y", time.localtime(start_psutil)))
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not MACOS, "MACOS only")
|
|
||||||
class TestZombieProcessAPIs(unittest.TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
zpid = create_zombie_proc()
|
|
||||||
cls.p = psutil.Process(zpid)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children(recursive=True)
|
|
||||||
|
|
||||||
def test_pidtask_info(self):
|
|
||||||
self.assertEqual(self.p.status(), psutil.STATUS_ZOMBIE)
|
|
||||||
self.p.ppid()
|
|
||||||
self.p.uids()
|
|
||||||
self.p.gids()
|
|
||||||
self.p.terminal()
|
|
||||||
self.p.create_time()
|
|
||||||
|
|
||||||
def test_exe(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.exe)
|
|
||||||
|
|
||||||
def test_cmdline(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.cmdline)
|
|
||||||
|
|
||||||
def test_environ(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.environ)
|
|
||||||
|
|
||||||
def test_cwd(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.cwd)
|
|
||||||
|
|
||||||
def test_memory_full_info(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.memory_full_info)
|
|
||||||
|
|
||||||
def test_cpu_times(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.cpu_times)
|
|
||||||
|
|
||||||
def test_num_ctx_switches(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.num_ctx_switches)
|
|
||||||
|
|
||||||
def test_num_threads(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.num_threads)
|
|
||||||
|
|
||||||
def test_open_files(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.open_files)
|
|
||||||
|
|
||||||
def test_connections(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.connections)
|
|
||||||
|
|
||||||
def test_num_fds(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.num_fds)
|
|
||||||
|
|
||||||
def test_threads(self):
|
|
||||||
self.assertRaises((psutil.ZombieProcess, psutil.AccessDenied),
|
|
||||||
self.p.threads)
|
|
||||||
|
|
||||||
def test_memory_maps(self):
|
|
||||||
self.assertRaises(psutil.ZombieProcess, self.p.memory_maps)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not MACOS, "MACOS only")
|
|
||||||
class TestSystemAPIs(unittest.TestCase):
|
|
||||||
|
|
||||||
# --- disk
|
|
||||||
|
|
||||||
def test_disks(self):
|
|
||||||
# test psutil.disk_usage() and psutil.disk_partitions()
|
|
||||||
# against "df -a"
|
|
||||||
def df(path):
|
|
||||||
out = sh('df -k "%s"' % path).strip()
|
|
||||||
lines = out.split('\n')
|
|
||||||
lines.pop(0)
|
|
||||||
line = lines.pop(0)
|
|
||||||
dev, total, used, free = line.split()[:4]
|
|
||||||
if dev == 'none':
|
|
||||||
dev = ''
|
|
||||||
total = int(total) * 1024
|
|
||||||
used = int(used) * 1024
|
|
||||||
free = int(free) * 1024
|
|
||||||
return dev, total, used, free
|
|
||||||
|
|
||||||
for part in psutil.disk_partitions(all=False):
|
|
||||||
usage = psutil.disk_usage(part.mountpoint)
|
|
||||||
dev, total, used, free = df(part.mountpoint)
|
|
||||||
self.assertEqual(part.device, dev)
|
|
||||||
self.assertEqual(usage.total, total)
|
|
||||||
# 10 MB tollerance
|
|
||||||
if abs(usage.free - free) > 10 * 1024 * 1024:
|
|
||||||
self.fail("psutil=%s, df=%s" % usage.free, free)
|
|
||||||
if abs(usage.used - used) > 10 * 1024 * 1024:
|
|
||||||
self.fail("psutil=%s, df=%s" % usage.used, used)
|
|
||||||
|
|
||||||
# --- cpu
|
|
||||||
|
|
||||||
def test_cpu_count_logical(self):
|
|
||||||
num = sysctl("sysctl hw.logicalcpu")
|
|
||||||
self.assertEqual(num, psutil.cpu_count(logical=True))
|
|
||||||
|
|
||||||
def test_cpu_count_physical(self):
|
|
||||||
num = sysctl("sysctl hw.physicalcpu")
|
|
||||||
self.assertEqual(num, psutil.cpu_count(logical=False))
|
|
||||||
|
|
||||||
def test_cpu_freq(self):
|
|
||||||
freq = psutil.cpu_freq()
|
|
||||||
self.assertEqual(
|
|
||||||
freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency"))
|
|
||||||
self.assertEqual(
|
|
||||||
freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min"))
|
|
||||||
self.assertEqual(
|
|
||||||
freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max"))
|
|
||||||
|
|
||||||
# --- virtual mem
|
|
||||||
|
|
||||||
def test_vmem_total(self):
|
|
||||||
sysctl_hwphymem = sysctl('sysctl hw.memsize')
|
|
||||||
self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_free(self):
|
|
||||||
vmstat_val = vm_stat("free")
|
|
||||||
psutil_val = psutil.virtual_memory().free
|
|
||||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_available(self):
|
|
||||||
vmstat_val = vm_stat("inactive") + vm_stat("free")
|
|
||||||
psutil_val = psutil.virtual_memory().available
|
|
||||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_active(self):
|
|
||||||
vmstat_val = vm_stat("active")
|
|
||||||
psutil_val = psutil.virtual_memory().active
|
|
||||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_inactive(self):
|
|
||||||
vmstat_val = vm_stat("inactive")
|
|
||||||
psutil_val = psutil.virtual_memory().inactive
|
|
||||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vmem_wired(self):
|
|
||||||
vmstat_val = vm_stat("wired")
|
|
||||||
psutil_val = psutil.virtual_memory().wired
|
|
||||||
self.assertAlmostEqual(psutil_val, vmstat_val, delta=MEMORY_TOLERANCE)
|
|
||||||
|
|
||||||
# --- swap mem
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_swapmem_sin(self):
|
|
||||||
vmstat_val = vm_stat("Pageins")
|
|
||||||
psutil_val = psutil.swap_memory().sin
|
|
||||||
self.assertEqual(psutil_val, vmstat_val)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_swapmem_sout(self):
|
|
||||||
vmstat_val = vm_stat("Pageout")
|
|
||||||
psutil_val = psutil.swap_memory().sout
|
|
||||||
self.assertEqual(psutil_val, vmstat_val)
|
|
||||||
|
|
||||||
# Not very reliable.
|
|
||||||
# def test_swapmem_total(self):
|
|
||||||
# out = sh('sysctl vm.swapusage')
|
|
||||||
# out = out.replace('vm.swapusage: ', '')
|
|
||||||
# total, used, free = re.findall('\d+.\d+\w', out)
|
|
||||||
# psutil_smem = psutil.swap_memory()
|
|
||||||
# self.assertEqual(psutil_smem.total, human2bytes(total))
|
|
||||||
# self.assertEqual(psutil_smem.used, human2bytes(used))
|
|
||||||
# self.assertEqual(psutil_smem.free, human2bytes(free))
|
|
||||||
|
|
||||||
# --- network
|
|
||||||
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
for name, stats in psutil.net_if_stats().items():
|
|
||||||
try:
|
|
||||||
out = sh("ifconfig %s" % name)
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
|
|
||||||
self.assertEqual(stats.mtu,
|
|
||||||
int(re.findall(r'mtu (\d+)', out)[0]))
|
|
||||||
|
|
||||||
# --- sensors_battery
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_sensors_battery(self):
|
|
||||||
out = sh("pmset -g batt")
|
|
||||||
percent = re.search("(\d+)%", out).group(1)
|
|
||||||
drawing_from = re.search("Now drawing from '([^']+)'", out).group(1)
|
|
||||||
power_plugged = drawing_from == "AC Power"
|
|
||||||
psutil_result = psutil.sensors_battery()
|
|
||||||
self.assertEqual(psutil_result.power_plugged, power_plugged)
|
|
||||||
self.assertEqual(psutil_result.percent, int(percent))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,418 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""POSIX specific tests."""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import AIX
|
|
||||||
from psutil import BSD
|
|
||||||
from psutil import LINUX
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil._compat import PY3
|
|
||||||
from psutil.tests import APPVEYOR
|
|
||||||
from psutil.tests import get_kernel_version
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import mock
|
|
||||||
from psutil.tests import PYTHON_EXE
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import retry_before_failing
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import skip_on_access_denied
|
|
||||||
from psutil.tests import TRAVIS
|
|
||||||
from psutil.tests import unittest
|
|
||||||
from psutil.tests import wait_for_pid
|
|
||||||
from psutil.tests import which
|
|
||||||
|
|
||||||
|
|
||||||
def ps(cmd):
|
|
||||||
"""Expects a ps command with a -o argument and parse the result
|
|
||||||
returning only the value of interest.
|
|
||||||
"""
|
|
||||||
if not LINUX:
|
|
||||||
cmd = cmd.replace(" --no-headers ", " ")
|
|
||||||
if SUNOS:
|
|
||||||
cmd = cmd.replace("-o start", "-o stime")
|
|
||||||
if AIX:
|
|
||||||
cmd = cmd.replace("-o rss", "-o rssize")
|
|
||||||
output = sh(cmd)
|
|
||||||
if not LINUX:
|
|
||||||
output = output.split('\n')[1].strip()
|
|
||||||
try:
|
|
||||||
return int(output)
|
|
||||||
except ValueError:
|
|
||||||
return output
|
|
||||||
|
|
||||||
# ps "-o" field names differ wildly between platforms.
|
|
||||||
# "comm" means "only executable name" but is not available on BSD platforms.
|
|
||||||
# "args" means "command with all its arguments", and is also not available
|
|
||||||
# on BSD platforms.
|
|
||||||
# "command" is like "args" on most platforms, but like "comm" on AIX,
|
|
||||||
# and not available on SUNOS.
|
|
||||||
# so for the executable name we can use "comm" on Solaris and split "command"
|
|
||||||
# on other platforms.
|
|
||||||
# to get the cmdline (with args) we have to use "args" on AIX and
|
|
||||||
# Solaris, and can use "command" on all others.
|
|
||||||
|
|
||||||
|
|
||||||
def ps_name(pid):
|
|
||||||
field = "command"
|
|
||||||
if SUNOS:
|
|
||||||
field = "comm"
|
|
||||||
return ps("ps --no-headers -o %s -p %s" % (field, pid)).split(' ')[0]
|
|
||||||
|
|
||||||
|
|
||||||
def ps_args(pid):
|
|
||||||
field = "command"
|
|
||||||
if AIX or SUNOS:
|
|
||||||
field = "args"
|
|
||||||
return ps("ps --no-headers -o %s -p %s" % (field, pid))
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
class TestProcess(unittest.TestCase):
|
|
||||||
"""Compare psutil results against 'ps' command line utility (mainly)."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess([PYTHON_EXE, "-E", "-O"],
|
|
||||||
stdin=subprocess.PIPE).pid
|
|
||||||
wait_for_pid(cls.pid)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_ppid(self):
|
|
||||||
ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid)
|
|
||||||
ppid_psutil = psutil.Process(self.pid).ppid()
|
|
||||||
self.assertEqual(ppid_ps, ppid_psutil)
|
|
||||||
|
|
||||||
def test_uid(self):
|
|
||||||
uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid)
|
|
||||||
uid_psutil = psutil.Process(self.pid).uids().real
|
|
||||||
self.assertEqual(uid_ps, uid_psutil)
|
|
||||||
|
|
||||||
def test_gid(self):
|
|
||||||
gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid)
|
|
||||||
gid_psutil = psutil.Process(self.pid).gids().real
|
|
||||||
self.assertEqual(gid_ps, gid_psutil)
|
|
||||||
|
|
||||||
def test_username(self):
|
|
||||||
username_ps = ps("ps --no-headers -o user -p %s" % self.pid)
|
|
||||||
username_psutil = psutil.Process(self.pid).username()
|
|
||||||
self.assertEqual(username_ps, username_psutil)
|
|
||||||
|
|
||||||
def test_username_no_resolution(self):
|
|
||||||
# Emulate a case where the system can't resolve the uid to
|
|
||||||
# a username in which case psutil is supposed to return
|
|
||||||
# the stringified uid.
|
|
||||||
p = psutil.Process()
|
|
||||||
with mock.patch("psutil.pwd.getpwuid", side_effect=KeyError) as fun:
|
|
||||||
self.assertEqual(p.username(), str(p.uids().real))
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
@skip_on_access_denied()
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_rss_memory(self):
|
|
||||||
# give python interpreter some time to properly initialize
|
|
||||||
# so that the results are the same
|
|
||||||
time.sleep(0.1)
|
|
||||||
rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid)
|
|
||||||
rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024
|
|
||||||
self.assertEqual(rss_ps, rss_psutil)
|
|
||||||
|
|
||||||
@skip_on_access_denied()
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_vsz_memory(self):
|
|
||||||
# give python interpreter some time to properly initialize
|
|
||||||
# so that the results are the same
|
|
||||||
time.sleep(0.1)
|
|
||||||
vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid)
|
|
||||||
vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024
|
|
||||||
self.assertEqual(vsz_ps, vsz_psutil)
|
|
||||||
|
|
||||||
def test_name(self):
|
|
||||||
name_ps = ps_name(self.pid)
|
|
||||||
# remove path if there is any, from the command
|
|
||||||
name_ps = os.path.basename(name_ps).lower()
|
|
||||||
name_psutil = psutil.Process(self.pid).name().lower()
|
|
||||||
# ...because of how we calculate PYTHON_EXE; on MACOS this may
|
|
||||||
# be "pythonX.Y".
|
|
||||||
name_ps = re.sub(r"\d.\d", "", name_ps)
|
|
||||||
name_psutil = re.sub(r"\d.\d", "", name_psutil)
|
|
||||||
self.assertEqual(name_ps, name_psutil)
|
|
||||||
|
|
||||||
def test_name_long(self):
|
|
||||||
# On UNIX the kernel truncates the name to the first 15
|
|
||||||
# characters. In such a case psutil tries to determine the
|
|
||||||
# full name from the cmdline.
|
|
||||||
name = "long-program-name"
|
|
||||||
cmdline = ["long-program-name-extended", "foo", "bar"]
|
|
||||||
with mock.patch("psutil._psplatform.Process.name",
|
|
||||||
return_value=name):
|
|
||||||
with mock.patch("psutil._psplatform.Process.cmdline",
|
|
||||||
return_value=cmdline):
|
|
||||||
p = psutil.Process()
|
|
||||||
self.assertEqual(p.name(), "long-program-name-extended")
|
|
||||||
|
|
||||||
def test_name_long_cmdline_ad_exc(self):
|
|
||||||
# Same as above but emulates a case where cmdline() raises
|
|
||||||
# AccessDenied in which case psutil is supposed to return
|
|
||||||
# the truncated name instead of crashing.
|
|
||||||
name = "long-program-name"
|
|
||||||
with mock.patch("psutil._psplatform.Process.name",
|
|
||||||
return_value=name):
|
|
||||||
with mock.patch("psutil._psplatform.Process.cmdline",
|
|
||||||
side_effect=psutil.AccessDenied(0, "")):
|
|
||||||
p = psutil.Process()
|
|
||||||
self.assertEqual(p.name(), "long-program-name")
|
|
||||||
|
|
||||||
def test_name_long_cmdline_nsp_exc(self):
|
|
||||||
# Same as above but emulates a case where cmdline() raises NSP
|
|
||||||
# which is supposed to propagate.
|
|
||||||
name = "long-program-name"
|
|
||||||
with mock.patch("psutil._psplatform.Process.name",
|
|
||||||
return_value=name):
|
|
||||||
with mock.patch("psutil._psplatform.Process.cmdline",
|
|
||||||
side_effect=psutil.NoSuchProcess(0, "")):
|
|
||||||
p = psutil.Process()
|
|
||||||
self.assertRaises(psutil.NoSuchProcess, p.name)
|
|
||||||
|
|
||||||
@unittest.skipIf(MACOS or BSD, 'ps -o start not available')
|
|
||||||
def test_create_time(self):
|
|
||||||
time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0]
|
|
||||||
time_psutil = psutil.Process(self.pid).create_time()
|
|
||||||
time_psutil_tstamp = datetime.datetime.fromtimestamp(
|
|
||||||
time_psutil).strftime("%H:%M:%S")
|
|
||||||
# sometimes ps shows the time rounded up instead of down, so we check
|
|
||||||
# for both possible values
|
|
||||||
round_time_psutil = round(time_psutil)
|
|
||||||
round_time_psutil_tstamp = datetime.datetime.fromtimestamp(
|
|
||||||
round_time_psutil).strftime("%H:%M:%S")
|
|
||||||
self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp])
|
|
||||||
|
|
||||||
def test_exe(self):
|
|
||||||
ps_pathname = ps_name(self.pid)
|
|
||||||
psutil_pathname = psutil.Process(self.pid).exe()
|
|
||||||
try:
|
|
||||||
self.assertEqual(ps_pathname, psutil_pathname)
|
|
||||||
except AssertionError:
|
|
||||||
# certain platforms such as BSD are more accurate returning:
|
|
||||||
# "/usr/local/bin/python2.7"
|
|
||||||
# ...instead of:
|
|
||||||
# "/usr/local/bin/python"
|
|
||||||
# We do not want to consider this difference in accuracy
|
|
||||||
# an error.
|
|
||||||
adjusted_ps_pathname = ps_pathname[:len(ps_pathname)]
|
|
||||||
self.assertEqual(ps_pathname, adjusted_ps_pathname)
|
|
||||||
|
|
||||||
def test_cmdline(self):
|
|
||||||
ps_cmdline = ps_args(self.pid)
|
|
||||||
psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline())
|
|
||||||
self.assertEqual(ps_cmdline, psutil_cmdline)
|
|
||||||
|
|
||||||
# On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an
|
|
||||||
# incorrect value (20); the real deal is getpriority(2) which
|
|
||||||
# returns 0; psutil relies on it, see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1082
|
|
||||||
# AIX has the same issue
|
|
||||||
@unittest.skipIf(SUNOS, "not reliable on SUNOS")
|
|
||||||
@unittest.skipIf(AIX, "not reliable on AIX")
|
|
||||||
def test_nice(self):
|
|
||||||
ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid)
|
|
||||||
psutil_nice = psutil.Process().nice()
|
|
||||||
self.assertEqual(ps_nice, psutil_nice)
|
|
||||||
|
|
||||||
def test_num_fds(self):
|
|
||||||
# Note: this fails from time to time; I'm keen on thinking
|
|
||||||
# it doesn't mean something is broken
|
|
||||||
def call(p, attr):
|
|
||||||
args = ()
|
|
||||||
attr = getattr(p, name, None)
|
|
||||||
if attr is not None and callable(attr):
|
|
||||||
if name == 'rlimit':
|
|
||||||
args = (psutil.RLIMIT_NOFILE,)
|
|
||||||
attr(*args)
|
|
||||||
else:
|
|
||||||
attr
|
|
||||||
|
|
||||||
p = psutil.Process(os.getpid())
|
|
||||||
failures = []
|
|
||||||
ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice',
|
|
||||||
'send_signal', 'wait', 'children', 'as_dict',
|
|
||||||
'memory_info_ex']
|
|
||||||
if LINUX and get_kernel_version() < (2, 6, 36):
|
|
||||||
ignored_names.append('rlimit')
|
|
||||||
if LINUX and get_kernel_version() < (2, 6, 23):
|
|
||||||
ignored_names.append('num_ctx_switches')
|
|
||||||
for name in dir(psutil.Process):
|
|
||||||
if (name.startswith('_') or name in ignored_names):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
num1 = p.num_fds()
|
|
||||||
for x in range(2):
|
|
||||||
call(p, name)
|
|
||||||
num2 = p.num_fds()
|
|
||||||
except psutil.AccessDenied:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if abs(num2 - num1) > 1:
|
|
||||||
fail = "failure while processing Process.%s method " \
|
|
||||||
"(before=%s, after=%s)" % (name, num1, num2)
|
|
||||||
failures.append(fail)
|
|
||||||
if failures:
|
|
||||||
self.fail('\n' + '\n'.join(failures))
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
class TestSystemAPIs(unittest.TestCase):
|
|
||||||
"""Test some system APIs."""
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_pids(self):
|
|
||||||
# Note: this test might fail if the OS is starting/killing
|
|
||||||
# other processes in the meantime
|
|
||||||
if SUNOS or AIX:
|
|
||||||
cmd = ["ps", "-A", "-o", "pid"]
|
|
||||||
else:
|
|
||||||
cmd = ["ps", "ax", "-o", "pid"]
|
|
||||||
p = get_test_subprocess(cmd, stdout=subprocess.PIPE)
|
|
||||||
output = p.communicate()[0].strip()
|
|
||||||
assert p.poll() == 0
|
|
||||||
if PY3:
|
|
||||||
output = str(output, sys.stdout.encoding)
|
|
||||||
pids_ps = []
|
|
||||||
for line in output.split('\n')[1:]:
|
|
||||||
if line:
|
|
||||||
pid = int(line.split()[0].strip())
|
|
||||||
pids_ps.append(pid)
|
|
||||||
# remove ps subprocess pid which is supposed to be dead in meantime
|
|
||||||
pids_ps.remove(p.pid)
|
|
||||||
pids_psutil = psutil.pids()
|
|
||||||
pids_ps.sort()
|
|
||||||
pids_psutil.sort()
|
|
||||||
|
|
||||||
# on MACOS and OPENBSD ps doesn't show pid 0
|
|
||||||
if MACOS or OPENBSD and 0 not in pids_ps:
|
|
||||||
pids_ps.insert(0, 0)
|
|
||||||
self.assertEqual(pids_ps, pids_psutil)
|
|
||||||
|
|
||||||
# for some reason ifconfig -a does not report all interfaces
|
|
||||||
# returned by psutil
|
|
||||||
@unittest.skipIf(SUNOS, "unreliable on SUNOS")
|
|
||||||
@unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
|
|
||||||
@unittest.skipIf(not which('ifconfig'), "no ifconfig cmd")
|
|
||||||
def test_nic_names(self):
|
|
||||||
output = sh("ifconfig -a")
|
|
||||||
for nic in psutil.net_io_counters(pernic=True).keys():
|
|
||||||
for line in output.split():
|
|
||||||
if line.startswith(nic):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.fail(
|
|
||||||
"couldn't find %s nic in 'ifconfig -a' output\n%s" % (
|
|
||||||
nic, output))
|
|
||||||
|
|
||||||
# can't find users on APPVEYOR or TRAVIS
|
|
||||||
@unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(),
|
|
||||||
"unreliable on APPVEYOR or TRAVIS")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_users(self):
|
|
||||||
out = sh("who")
|
|
||||||
lines = out.split('\n')
|
|
||||||
users = [x.split()[0] for x in lines]
|
|
||||||
terminals = [x.split()[1] for x in lines]
|
|
||||||
self.assertEqual(len(users), len(psutil.users()))
|
|
||||||
for u in psutil.users():
|
|
||||||
self.assertIn(u.name, users)
|
|
||||||
self.assertIn(u.terminal, terminals)
|
|
||||||
|
|
||||||
def test_pid_exists_let_raise(self):
|
|
||||||
# According to "man 2 kill" possible error values for kill
|
|
||||||
# are (EINVAL, EPERM, ESRCH). Test that any other errno
|
|
||||||
# results in an exception.
|
|
||||||
with mock.patch("psutil._psposix.os.kill",
|
|
||||||
side_effect=OSError(errno.EBADF, "")) as m:
|
|
||||||
self.assertRaises(OSError, psutil._psposix.pid_exists, os.getpid())
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_os_waitpid_let_raise(self):
|
|
||||||
# os.waitpid() is supposed to catch EINTR and ECHILD only.
|
|
||||||
# Test that any other errno results in an exception.
|
|
||||||
with mock.patch("psutil._psposix.os.waitpid",
|
|
||||||
side_effect=OSError(errno.EBADF, "")) as m:
|
|
||||||
self.assertRaises(OSError, psutil._psposix.wait_pid, os.getpid())
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_os_waitpid_eintr(self):
|
|
||||||
# os.waitpid() is supposed to "retry" on EINTR.
|
|
||||||
with mock.patch("psutil._psposix.os.waitpid",
|
|
||||||
side_effect=OSError(errno.EINTR, "")) as m:
|
|
||||||
self.assertRaises(
|
|
||||||
psutil._psposix.TimeoutExpired,
|
|
||||||
psutil._psposix.wait_pid, os.getpid(), timeout=0.01)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_os_waitpid_bad_ret_status(self):
|
|
||||||
# Simulate os.waitpid() returning a bad status.
|
|
||||||
with mock.patch("psutil._psposix.os.waitpid",
|
|
||||||
return_value=(1, -1)) as m:
|
|
||||||
self.assertRaises(ValueError,
|
|
||||||
psutil._psposix.wait_pid, os.getpid())
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
# AIX can return '-' in df output instead of numbers, e.g. for /proc
|
|
||||||
@unittest.skipIf(AIX, "unreliable on AIX")
|
|
||||||
def test_disk_usage(self):
|
|
||||||
def df(device):
|
|
||||||
out = sh("df -k %s" % device).strip()
|
|
||||||
line = out.split('\n')[1]
|
|
||||||
fields = line.split()
|
|
||||||
total = int(fields[1]) * 1024
|
|
||||||
used = int(fields[2]) * 1024
|
|
||||||
free = int(fields[3]) * 1024
|
|
||||||
percent = float(fields[4].replace('%', ''))
|
|
||||||
return (total, used, free, percent)
|
|
||||||
|
|
||||||
tolerance = 4 * 1024 * 1024 # 4MB
|
|
||||||
for part in psutil.disk_partitions(all=False):
|
|
||||||
usage = psutil.disk_usage(part.mountpoint)
|
|
||||||
try:
|
|
||||||
total, used, free, percent = df(part.device)
|
|
||||||
except RuntimeError as err:
|
|
||||||
# see:
|
|
||||||
# https://travis-ci.org/giampaolo/psutil/jobs/138338464
|
|
||||||
# https://travis-ci.org/giampaolo/psutil/jobs/138343361
|
|
||||||
err = str(err).lower()
|
|
||||||
if "no such file or directory" in err or \
|
|
||||||
"raw devices not supported" in err or \
|
|
||||||
"permission denied" in err:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
self.assertAlmostEqual(usage.total, total, delta=tolerance)
|
|
||||||
self.assertAlmostEqual(usage.used, used, delta=tolerance)
|
|
||||||
self.assertAlmostEqual(usage.free, free, delta=tolerance)
|
|
||||||
self.assertAlmostEqual(usage.percent, percent, delta=1)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,45 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Sun OS specific tests."""
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import unittest
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not SUNOS, "SUNOS only")
|
|
||||||
class SunOSSpecificTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_swap_memory(self):
|
|
||||||
out = sh('env PATH=/usr/sbin:/sbin:%s swap -l' % os.environ['PATH'])
|
|
||||||
lines = out.strip().split('\n')[1:]
|
|
||||||
if not lines:
|
|
||||||
raise ValueError('no swap device(s) configured')
|
|
||||||
total = free = 0
|
|
||||||
for line in lines:
|
|
||||||
line = line.split()
|
|
||||||
t, f = line[-2:]
|
|
||||||
total += int(int(t) * 512)
|
|
||||||
free += int(int(f) * 512)
|
|
||||||
used = total - free
|
|
||||||
|
|
||||||
psutil_swap = psutil.swap_memory()
|
|
||||||
self.assertEqual(psutil_swap.total, total)
|
|
||||||
self.assertEqual(psutil_swap.used, used)
|
|
||||||
self.assertEqual(psutil_swap.free, free)
|
|
||||||
|
|
||||||
def test_cpu_count(self):
|
|
||||||
out = sh("/usr/sbin/psrinfo")
|
|
||||||
self.assertEqual(psutil.cpu_count(), len(out.split('\n')))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,869 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Tests for system APIS."""
|
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import datetime
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import pprint
|
|
||||||
import shutil
|
|
||||||
import signal
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import AIX
|
|
||||||
from psutil import BSD
|
|
||||||
from psutil import FREEBSD
|
|
||||||
from psutil import LINUX
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import NETBSD
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import SUNOS
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil._compat import long
|
|
||||||
from psutil.tests import APPVEYOR
|
|
||||||
from psutil.tests import ASCII_FS
|
|
||||||
from psutil.tests import check_net_address
|
|
||||||
from psutil.tests import DEVNULL
|
|
||||||
from psutil.tests import enum
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_BATTERY
|
|
||||||
from psutil.tests import HAS_CPU_FREQ
|
|
||||||
from psutil.tests import HAS_SENSORS_BATTERY
|
|
||||||
from psutil.tests import HAS_SENSORS_FANS
|
|
||||||
from psutil.tests import HAS_SENSORS_TEMPERATURES
|
|
||||||
from psutil.tests import mock
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import retry_before_failing
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import safe_rmpath
|
|
||||||
from psutil.tests import TESTFN
|
|
||||||
from psutil.tests import TESTFN_UNICODE
|
|
||||||
from psutil.tests import TRAVIS
|
|
||||||
from psutil.tests import unittest
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# --- System-related API tests
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestSystemAPIs(unittest.TestCase):
|
|
||||||
"""Tests for system-related APIs."""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
safe_rmpath(TESTFN)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_process_iter(self):
|
|
||||||
self.assertIn(os.getpid(), [x.pid for x in psutil.process_iter()])
|
|
||||||
sproc = get_test_subprocess()
|
|
||||||
self.assertIn(sproc.pid, [x.pid for x in psutil.process_iter()])
|
|
||||||
p = psutil.Process(sproc.pid)
|
|
||||||
p.kill()
|
|
||||||
p.wait()
|
|
||||||
self.assertNotIn(sproc.pid, [x.pid for x in psutil.process_iter()])
|
|
||||||
|
|
||||||
with mock.patch('psutil.Process',
|
|
||||||
side_effect=psutil.NoSuchProcess(os.getpid())):
|
|
||||||
self.assertEqual(list(psutil.process_iter()), [])
|
|
||||||
with mock.patch('psutil.Process',
|
|
||||||
side_effect=psutil.AccessDenied(os.getpid())):
|
|
||||||
with self.assertRaises(psutil.AccessDenied):
|
|
||||||
list(psutil.process_iter())
|
|
||||||
|
|
||||||
def test_prcess_iter_w_params(self):
|
|
||||||
for p in psutil.process_iter(attrs=['pid']):
|
|
||||||
self.assertEqual(list(p.info.keys()), ['pid'])
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
list(psutil.process_iter(attrs=['foo']))
|
|
||||||
with mock.patch("psutil._psplatform.Process.cpu_times",
|
|
||||||
side_effect=psutil.AccessDenied(0, "")) as m:
|
|
||||||
for p in psutil.process_iter(attrs=["pid", "cpu_times"]):
|
|
||||||
self.assertIsNone(p.info['cpu_times'])
|
|
||||||
self.assertGreaterEqual(p.info['pid'], 0)
|
|
||||||
assert m.called
|
|
||||||
with mock.patch("psutil._psplatform.Process.cpu_times",
|
|
||||||
side_effect=psutil.AccessDenied(0, "")) as m:
|
|
||||||
flag = object()
|
|
||||||
for p in psutil.process_iter(
|
|
||||||
attrs=["pid", "cpu_times"], ad_value=flag):
|
|
||||||
self.assertIs(p.info['cpu_times'], flag)
|
|
||||||
self.assertGreaterEqual(p.info['pid'], 0)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_wait_procs(self):
|
|
||||||
def callback(p):
|
|
||||||
pids.append(p.pid)
|
|
||||||
|
|
||||||
pids = []
|
|
||||||
sproc1 = get_test_subprocess()
|
|
||||||
sproc2 = get_test_subprocess()
|
|
||||||
sproc3 = get_test_subprocess()
|
|
||||||
procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
|
|
||||||
self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1)
|
|
||||||
self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1)
|
|
||||||
t = time.time()
|
|
||||||
gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback)
|
|
||||||
|
|
||||||
self.assertLess(time.time() - t, 0.5)
|
|
||||||
self.assertEqual(gone, [])
|
|
||||||
self.assertEqual(len(alive), 3)
|
|
||||||
self.assertEqual(pids, [])
|
|
||||||
for p in alive:
|
|
||||||
self.assertFalse(hasattr(p, 'returncode'))
|
|
||||||
|
|
||||||
@retry_before_failing(30)
|
|
||||||
def test(procs, callback):
|
|
||||||
gone, alive = psutil.wait_procs(procs, timeout=0.03,
|
|
||||||
callback=callback)
|
|
||||||
self.assertEqual(len(gone), 1)
|
|
||||||
self.assertEqual(len(alive), 2)
|
|
||||||
return gone, alive
|
|
||||||
|
|
||||||
sproc3.terminate()
|
|
||||||
gone, alive = test(procs, callback)
|
|
||||||
self.assertIn(sproc3.pid, [x.pid for x in gone])
|
|
||||||
if POSIX:
|
|
||||||
self.assertEqual(gone.pop().returncode, -signal.SIGTERM)
|
|
||||||
else:
|
|
||||||
self.assertEqual(gone.pop().returncode, 1)
|
|
||||||
self.assertEqual(pids, [sproc3.pid])
|
|
||||||
for p in alive:
|
|
||||||
self.assertFalse(hasattr(p, 'returncode'))
|
|
||||||
|
|
||||||
@retry_before_failing(30)
|
|
||||||
def test(procs, callback):
|
|
||||||
gone, alive = psutil.wait_procs(procs, timeout=0.03,
|
|
||||||
callback=callback)
|
|
||||||
self.assertEqual(len(gone), 3)
|
|
||||||
self.assertEqual(len(alive), 0)
|
|
||||||
return gone, alive
|
|
||||||
|
|
||||||
sproc1.terminate()
|
|
||||||
sproc2.terminate()
|
|
||||||
gone, alive = test(procs, callback)
|
|
||||||
self.assertEqual(set(pids), set([sproc1.pid, sproc2.pid, sproc3.pid]))
|
|
||||||
for p in gone:
|
|
||||||
self.assertTrue(hasattr(p, 'returncode'))
|
|
||||||
|
|
||||||
def test_wait_procs_no_timeout(self):
|
|
||||||
sproc1 = get_test_subprocess()
|
|
||||||
sproc2 = get_test_subprocess()
|
|
||||||
sproc3 = get_test_subprocess()
|
|
||||||
procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)]
|
|
||||||
for p in procs:
|
|
||||||
p.terminate()
|
|
||||||
gone, alive = psutil.wait_procs(procs)
|
|
||||||
|
|
||||||
def test_boot_time(self):
|
|
||||||
bt = psutil.boot_time()
|
|
||||||
self.assertIsInstance(bt, float)
|
|
||||||
self.assertGreater(bt, 0)
|
|
||||||
self.assertLess(bt, time.time())
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, 'POSIX only')
|
|
||||||
def test_PAGESIZE(self):
|
|
||||||
# pagesize is used internally to perform different calculations
|
|
||||||
# and it's determined by using SC_PAGE_SIZE; make sure
|
|
||||||
# getpagesize() returns the same value.
|
|
||||||
import resource
|
|
||||||
self.assertEqual(os.sysconf("SC_PAGE_SIZE"), resource.getpagesize())
|
|
||||||
|
|
||||||
def test_virtual_memory(self):
|
|
||||||
mem = psutil.virtual_memory()
|
|
||||||
assert mem.total > 0, mem
|
|
||||||
assert mem.available > 0, mem
|
|
||||||
assert 0 <= mem.percent <= 100, mem
|
|
||||||
assert mem.used > 0, mem
|
|
||||||
assert mem.free >= 0, mem
|
|
||||||
for name in mem._fields:
|
|
||||||
value = getattr(mem, name)
|
|
||||||
if name != 'percent':
|
|
||||||
self.assertIsInstance(value, (int, long))
|
|
||||||
if name != 'total':
|
|
||||||
if not value >= 0:
|
|
||||||
self.fail("%r < 0 (%s)" % (name, value))
|
|
||||||
if value > mem.total:
|
|
||||||
self.fail("%r > total (total=%s, %s=%s)"
|
|
||||||
% (name, mem.total, name, value))
|
|
||||||
|
|
||||||
def test_swap_memory(self):
|
|
||||||
mem = psutil.swap_memory()
|
|
||||||
self.assertEqual(
|
|
||||||
mem._fields, ('total', 'used', 'free', 'percent', 'sin', 'sout'))
|
|
||||||
|
|
||||||
assert mem.total >= 0, mem
|
|
||||||
assert mem.used >= 0, mem
|
|
||||||
if mem.total > 0:
|
|
||||||
# likely a system with no swap partition
|
|
||||||
assert mem.free > 0, mem
|
|
||||||
else:
|
|
||||||
assert mem.free == 0, mem
|
|
||||||
assert 0 <= mem.percent <= 100, mem
|
|
||||||
assert mem.sin >= 0, mem
|
|
||||||
assert mem.sout >= 0, mem
|
|
||||||
|
|
||||||
def test_pid_exists(self):
|
|
||||||
sproc = get_test_subprocess()
|
|
||||||
self.assertTrue(psutil.pid_exists(sproc.pid))
|
|
||||||
p = psutil.Process(sproc.pid)
|
|
||||||
p.kill()
|
|
||||||
p.wait()
|
|
||||||
self.assertFalse(psutil.pid_exists(sproc.pid))
|
|
||||||
self.assertFalse(psutil.pid_exists(-1))
|
|
||||||
self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids())
|
|
||||||
|
|
||||||
def test_pid_exists_2(self):
|
|
||||||
reap_children()
|
|
||||||
pids = psutil.pids()
|
|
||||||
for pid in pids:
|
|
||||||
try:
|
|
||||||
assert psutil.pid_exists(pid)
|
|
||||||
except AssertionError:
|
|
||||||
# in case the process disappeared in meantime fail only
|
|
||||||
# if it is no longer in psutil.pids()
|
|
||||||
time.sleep(.1)
|
|
||||||
if pid in psutil.pids():
|
|
||||||
self.fail(pid)
|
|
||||||
pids = range(max(pids) + 5000, max(pids) + 6000)
|
|
||||||
for pid in pids:
|
|
||||||
self.assertFalse(psutil.pid_exists(pid), msg=pid)
|
|
||||||
|
|
||||||
def test_pids(self):
|
|
||||||
plist = [x.pid for x in psutil.process_iter()]
|
|
||||||
pidlist = psutil.pids()
|
|
||||||
self.assertEqual(plist.sort(), pidlist.sort())
|
|
||||||
# make sure every pid is unique
|
|
||||||
self.assertEqual(len(pidlist), len(set(pidlist)))
|
|
||||||
|
|
||||||
def test_test(self):
|
|
||||||
# test for psutil.test() function
|
|
||||||
stdout = sys.stdout
|
|
||||||
sys.stdout = DEVNULL
|
|
||||||
try:
|
|
||||||
psutil.test()
|
|
||||||
finally:
|
|
||||||
sys.stdout = stdout
|
|
||||||
|
|
||||||
def test_cpu_count(self):
|
|
||||||
logical = psutil.cpu_count()
|
|
||||||
self.assertEqual(logical, len(psutil.cpu_times(percpu=True)))
|
|
||||||
self.assertGreaterEqual(logical, 1)
|
|
||||||
#
|
|
||||||
if os.path.exists("/proc/cpuinfo"):
|
|
||||||
with open("/proc/cpuinfo") as fd:
|
|
||||||
cpuinfo_data = fd.read()
|
|
||||||
if "physical id" not in cpuinfo_data:
|
|
||||||
raise unittest.SkipTest("cpuinfo doesn't include physical id")
|
|
||||||
physical = psutil.cpu_count(logical=False)
|
|
||||||
if WINDOWS and sys.getwindowsversion()[:2] <= (6, 1): # <= Vista
|
|
||||||
self.assertIsNone(physical)
|
|
||||||
else:
|
|
||||||
self.assertGreaterEqual(physical, 1)
|
|
||||||
self.assertGreaterEqual(logical, physical)
|
|
||||||
|
|
||||||
def test_cpu_count_none(self):
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1085
|
|
||||||
for val in (-1, 0, None):
|
|
||||||
with mock.patch('psutil._psplatform.cpu_count_logical',
|
|
||||||
return_value=val) as m:
|
|
||||||
self.assertIsNone(psutil.cpu_count())
|
|
||||||
assert m.called
|
|
||||||
with mock.patch('psutil._psplatform.cpu_count_physical',
|
|
||||||
return_value=val) as m:
|
|
||||||
self.assertIsNone(psutil.cpu_count(logical=False))
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_cpu_times(self):
|
|
||||||
# Check type, value >= 0, str().
|
|
||||||
total = 0
|
|
||||||
times = psutil.cpu_times()
|
|
||||||
sum(times)
|
|
||||||
for cp_time in times:
|
|
||||||
self.assertIsInstance(cp_time, float)
|
|
||||||
self.assertGreaterEqual(cp_time, 0.0)
|
|
||||||
total += cp_time
|
|
||||||
self.assertEqual(total, sum(times))
|
|
||||||
str(times)
|
|
||||||
# CPU times are always supposed to increase over time
|
|
||||||
# or at least remain the same and that's because time
|
|
||||||
# cannot go backwards.
|
|
||||||
# Surprisingly sometimes this might not be the case (at
|
|
||||||
# least on Windows and Linux), see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/392
|
|
||||||
# https://github.com/giampaolo/psutil/issues/645
|
|
||||||
# if not WINDOWS:
|
|
||||||
# last = psutil.cpu_times()
|
|
||||||
# for x in range(100):
|
|
||||||
# new = psutil.cpu_times()
|
|
||||||
# for field in new._fields:
|
|
||||||
# new_t = getattr(new, field)
|
|
||||||
# last_t = getattr(last, field)
|
|
||||||
# self.assertGreaterEqual(new_t, last_t,
|
|
||||||
# msg="%s %s" % (new_t, last_t))
|
|
||||||
# last = new
|
|
||||||
|
|
||||||
def test_cpu_times_time_increases(self):
|
|
||||||
# Make sure time increases between calls.
|
|
||||||
t1 = sum(psutil.cpu_times())
|
|
||||||
time.sleep(0.1)
|
|
||||||
t2 = sum(psutil.cpu_times())
|
|
||||||
difference = t2 - t1
|
|
||||||
if not difference >= 0.05:
|
|
||||||
self.fail("difference %s" % difference)
|
|
||||||
|
|
||||||
def test_per_cpu_times(self):
|
|
||||||
# Check type, value >= 0, str().
|
|
||||||
for times in psutil.cpu_times(percpu=True):
|
|
||||||
total = 0
|
|
||||||
sum(times)
|
|
||||||
for cp_time in times:
|
|
||||||
self.assertIsInstance(cp_time, float)
|
|
||||||
self.assertGreaterEqual(cp_time, 0.0)
|
|
||||||
total += cp_time
|
|
||||||
self.assertEqual(total, sum(times))
|
|
||||||
str(times)
|
|
||||||
self.assertEqual(len(psutil.cpu_times(percpu=True)[0]),
|
|
||||||
len(psutil.cpu_times(percpu=False)))
|
|
||||||
|
|
||||||
# Note: in theory CPU times are always supposed to increase over
|
|
||||||
# time or remain the same but never go backwards. In practice
|
|
||||||
# sometimes this is not the case.
|
|
||||||
# This issue seemd to be afflict Windows:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/392
|
|
||||||
# ...but it turns out also Linux (rarely) behaves the same.
|
|
||||||
# last = psutil.cpu_times(percpu=True)
|
|
||||||
# for x in range(100):
|
|
||||||
# new = psutil.cpu_times(percpu=True)
|
|
||||||
# for index in range(len(new)):
|
|
||||||
# newcpu = new[index]
|
|
||||||
# lastcpu = last[index]
|
|
||||||
# for field in newcpu._fields:
|
|
||||||
# new_t = getattr(newcpu, field)
|
|
||||||
# last_t = getattr(lastcpu, field)
|
|
||||||
# self.assertGreaterEqual(
|
|
||||||
# new_t, last_t, msg="%s %s" % (lastcpu, newcpu))
|
|
||||||
# last = new
|
|
||||||
|
|
||||||
def test_per_cpu_times_2(self):
|
|
||||||
# Simulate some work load then make sure time have increased
|
|
||||||
# between calls.
|
|
||||||
tot1 = psutil.cpu_times(percpu=True)
|
|
||||||
stop_at = time.time() + 0.1
|
|
||||||
while True:
|
|
||||||
if time.time() >= stop_at:
|
|
||||||
break
|
|
||||||
tot2 = psutil.cpu_times(percpu=True)
|
|
||||||
for t1, t2 in zip(tot1, tot2):
|
|
||||||
t1, t2 = sum(t1), sum(t2)
|
|
||||||
difference = t2 - t1
|
|
||||||
if difference >= 0.05:
|
|
||||||
return
|
|
||||||
self.fail()
|
|
||||||
|
|
||||||
def test_cpu_times_comparison(self):
|
|
||||||
# Make sure the sum of all per cpu times is almost equal to
|
|
||||||
# base "one cpu" times.
|
|
||||||
base = psutil.cpu_times()
|
|
||||||
per_cpu = psutil.cpu_times(percpu=True)
|
|
||||||
summed_values = base._make([sum(num) for num in zip(*per_cpu)])
|
|
||||||
for field in base._fields:
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
getattr(base, field), getattr(summed_values, field), delta=1)
|
|
||||||
|
|
||||||
def _test_cpu_percent(self, percent, last_ret, new_ret):
|
|
||||||
try:
|
|
||||||
self.assertIsInstance(percent, float)
|
|
||||||
self.assertGreaterEqual(percent, 0.0)
|
|
||||||
self.assertIsNot(percent, -0.0)
|
|
||||||
self.assertLessEqual(percent, 100.0 * psutil.cpu_count())
|
|
||||||
except AssertionError as err:
|
|
||||||
raise AssertionError("\n%s\nlast=%s\nnew=%s" % (
|
|
||||||
err, pprint.pformat(last_ret), pprint.pformat(new_ret)))
|
|
||||||
|
|
||||||
def test_cpu_percent(self):
|
|
||||||
last = psutil.cpu_percent(interval=0.001)
|
|
||||||
for x in range(100):
|
|
||||||
new = psutil.cpu_percent(interval=None)
|
|
||||||
self._test_cpu_percent(new, last, new)
|
|
||||||
last = new
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
psutil.cpu_percent(interval=-1)
|
|
||||||
|
|
||||||
def test_per_cpu_percent(self):
|
|
||||||
last = psutil.cpu_percent(interval=0.001, percpu=True)
|
|
||||||
self.assertEqual(len(last), psutil.cpu_count())
|
|
||||||
for x in range(100):
|
|
||||||
new = psutil.cpu_percent(interval=None, percpu=True)
|
|
||||||
for percent in new:
|
|
||||||
self._test_cpu_percent(percent, last, new)
|
|
||||||
last = new
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
psutil.cpu_percent(interval=-1, percpu=True)
|
|
||||||
|
|
||||||
def test_cpu_times_percent(self):
|
|
||||||
last = psutil.cpu_times_percent(interval=0.001)
|
|
||||||
for x in range(100):
|
|
||||||
new = psutil.cpu_times_percent(interval=None)
|
|
||||||
for percent in new:
|
|
||||||
self._test_cpu_percent(percent, last, new)
|
|
||||||
self._test_cpu_percent(sum(new), last, new)
|
|
||||||
last = new
|
|
||||||
|
|
||||||
def test_per_cpu_times_percent(self):
|
|
||||||
last = psutil.cpu_times_percent(interval=0.001, percpu=True)
|
|
||||||
self.assertEqual(len(last), psutil.cpu_count())
|
|
||||||
for x in range(100):
|
|
||||||
new = psutil.cpu_times_percent(interval=None, percpu=True)
|
|
||||||
for cpu in new:
|
|
||||||
for percent in cpu:
|
|
||||||
self._test_cpu_percent(percent, last, new)
|
|
||||||
self._test_cpu_percent(sum(cpu), last, new)
|
|
||||||
last = new
|
|
||||||
|
|
||||||
def test_per_cpu_times_percent_negative(self):
|
|
||||||
# see: https://github.com/giampaolo/psutil/issues/645
|
|
||||||
psutil.cpu_times_percent(percpu=True)
|
|
||||||
zero_times = [x._make([0 for x in range(len(x._fields))])
|
|
||||||
for x in psutil.cpu_times(percpu=True)]
|
|
||||||
with mock.patch('psutil.cpu_times', return_value=zero_times):
|
|
||||||
for cpu in psutil.cpu_times_percent(percpu=True):
|
|
||||||
for percent in cpu:
|
|
||||||
self._test_cpu_percent(percent, None, None)
|
|
||||||
|
|
||||||
def test_disk_usage(self):
|
|
||||||
usage = psutil.disk_usage(os.getcwd())
|
|
||||||
self.assertEqual(usage._fields, ('total', 'used', 'free', 'percent'))
|
|
||||||
|
|
||||||
assert usage.total > 0, usage
|
|
||||||
assert usage.used > 0, usage
|
|
||||||
assert usage.free > 0, usage
|
|
||||||
assert usage.total > usage.used, usage
|
|
||||||
assert usage.total > usage.free, usage
|
|
||||||
assert 0 <= usage.percent <= 100, usage.percent
|
|
||||||
if hasattr(shutil, 'disk_usage'):
|
|
||||||
# py >= 3.3, see: http://bugs.python.org/issue12442
|
|
||||||
shutil_usage = shutil.disk_usage(os.getcwd())
|
|
||||||
tolerance = 5 * 1024 * 1024 # 5MB
|
|
||||||
self.assertEqual(usage.total, shutil_usage.total)
|
|
||||||
self.assertAlmostEqual(usage.free, shutil_usage.free,
|
|
||||||
delta=tolerance)
|
|
||||||
self.assertAlmostEqual(usage.used, shutil_usage.used,
|
|
||||||
delta=tolerance)
|
|
||||||
|
|
||||||
# if path does not exist OSError ENOENT is expected across
|
|
||||||
# all platforms
|
|
||||||
fname = tempfile.mktemp()
|
|
||||||
with self.assertRaises(OSError) as exc:
|
|
||||||
psutil.disk_usage(fname)
|
|
||||||
self.assertEqual(exc.exception.errno, errno.ENOENT)
|
|
||||||
|
|
||||||
def test_disk_usage_unicode(self):
|
|
||||||
# See: https://github.com/giampaolo/psutil/issues/416
|
|
||||||
if ASCII_FS:
|
|
||||||
with self.assertRaises(UnicodeEncodeError):
|
|
||||||
psutil.disk_usage(TESTFN_UNICODE)
|
|
||||||
|
|
||||||
def test_disk_usage_bytes(self):
|
|
||||||
psutil.disk_usage(b'.')
|
|
||||||
|
|
||||||
def test_disk_partitions(self):
|
|
||||||
# all = False
|
|
||||||
ls = psutil.disk_partitions(all=False)
|
|
||||||
# on travis we get:
|
|
||||||
# self.assertEqual(p.cpu_affinity(), [n])
|
|
||||||
# AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, 7,... != [0]
|
|
||||||
self.assertTrue(ls, msg=ls)
|
|
||||||
for disk in ls:
|
|
||||||
self.assertIsInstance(disk.device, str)
|
|
||||||
self.assertIsInstance(disk.mountpoint, str)
|
|
||||||
self.assertIsInstance(disk.fstype, str)
|
|
||||||
self.assertIsInstance(disk.opts, str)
|
|
||||||
if WINDOWS and 'cdrom' in disk.opts:
|
|
||||||
continue
|
|
||||||
if not POSIX:
|
|
||||||
assert os.path.exists(disk.device), disk
|
|
||||||
else:
|
|
||||||
# we cannot make any assumption about this, see:
|
|
||||||
# http://goo.gl/p9c43
|
|
||||||
disk.device
|
|
||||||
if SUNOS or TRAVIS:
|
|
||||||
# on solaris apparently mount points can also be files
|
|
||||||
assert os.path.exists(disk.mountpoint), disk
|
|
||||||
else:
|
|
||||||
assert os.path.isdir(disk.mountpoint), disk
|
|
||||||
assert disk.fstype, disk
|
|
||||||
|
|
||||||
# all = True
|
|
||||||
ls = psutil.disk_partitions(all=True)
|
|
||||||
self.assertTrue(ls, msg=ls)
|
|
||||||
for disk in psutil.disk_partitions(all=True):
|
|
||||||
if not WINDOWS:
|
|
||||||
try:
|
|
||||||
os.stat(disk.mountpoint)
|
|
||||||
except OSError as err:
|
|
||||||
if TRAVIS and MACOS and err.errno == errno.EIO:
|
|
||||||
continue
|
|
||||||
# http://mail.python.org/pipermail/python-dev/
|
|
||||||
# 2012-June/120787.html
|
|
||||||
if err.errno not in (errno.EPERM, errno.EACCES):
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
if SUNOS or TRAVIS:
|
|
||||||
# on solaris apparently mount points can also be files
|
|
||||||
assert os.path.exists(disk.mountpoint), disk
|
|
||||||
else:
|
|
||||||
assert os.path.isdir(disk.mountpoint), disk
|
|
||||||
self.assertIsInstance(disk.fstype, str)
|
|
||||||
self.assertIsInstance(disk.opts, str)
|
|
||||||
|
|
||||||
def find_mount_point(path):
|
|
||||||
path = os.path.abspath(path)
|
|
||||||
while not os.path.ismount(path):
|
|
||||||
path = os.path.dirname(path)
|
|
||||||
return path.lower()
|
|
||||||
|
|
||||||
mount = find_mount_point(__file__)
|
|
||||||
mounts = [x.mountpoint.lower() for x in
|
|
||||||
psutil.disk_partitions(all=True)]
|
|
||||||
self.assertIn(mount, mounts)
|
|
||||||
psutil.disk_usage(mount)
|
|
||||||
|
|
||||||
def test_net_io_counters(self):
|
|
||||||
def check_ntuple(nt):
|
|
||||||
self.assertEqual(nt[0], nt.bytes_sent)
|
|
||||||
self.assertEqual(nt[1], nt.bytes_recv)
|
|
||||||
self.assertEqual(nt[2], nt.packets_sent)
|
|
||||||
self.assertEqual(nt[3], nt.packets_recv)
|
|
||||||
self.assertEqual(nt[4], nt.errin)
|
|
||||||
self.assertEqual(nt[5], nt.errout)
|
|
||||||
self.assertEqual(nt[6], nt.dropin)
|
|
||||||
self.assertEqual(nt[7], nt.dropout)
|
|
||||||
assert nt.bytes_sent >= 0, nt
|
|
||||||
assert nt.bytes_recv >= 0, nt
|
|
||||||
assert nt.packets_sent >= 0, nt
|
|
||||||
assert nt.packets_recv >= 0, nt
|
|
||||||
assert nt.errin >= 0, nt
|
|
||||||
assert nt.errout >= 0, nt
|
|
||||||
assert nt.dropin >= 0, nt
|
|
||||||
assert nt.dropout >= 0, nt
|
|
||||||
|
|
||||||
ret = psutil.net_io_counters(pernic=False)
|
|
||||||
check_ntuple(ret)
|
|
||||||
ret = psutil.net_io_counters(pernic=True)
|
|
||||||
self.assertNotEqual(ret, [])
|
|
||||||
for key in ret:
|
|
||||||
self.assertTrue(key)
|
|
||||||
self.assertIsInstance(key, str)
|
|
||||||
check_ntuple(ret[key])
|
|
||||||
|
|
||||||
def test_net_io_counters_no_nics(self):
|
|
||||||
# Emulate a case where no NICs are installed, see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1062
|
|
||||||
with mock.patch('psutil._psplatform.net_io_counters',
|
|
||||||
return_value={}) as m:
|
|
||||||
self.assertIsNone(psutil.net_io_counters(pernic=False))
|
|
||||||
self.assertEqual(psutil.net_io_counters(pernic=True), {})
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_net_if_addrs(self):
|
|
||||||
nics = psutil.net_if_addrs()
|
|
||||||
assert nics, nics
|
|
||||||
|
|
||||||
nic_stats = psutil.net_if_stats()
|
|
||||||
|
|
||||||
# Not reliable on all platforms (net_if_addrs() reports more
|
|
||||||
# interfaces).
|
|
||||||
# self.assertEqual(sorted(nics.keys()),
|
|
||||||
# sorted(psutil.net_io_counters(pernic=True).keys()))
|
|
||||||
|
|
||||||
families = set([socket.AF_INET, socket.AF_INET6, psutil.AF_LINK])
|
|
||||||
for nic, addrs in nics.items():
|
|
||||||
self.assertIsInstance(nic, str)
|
|
||||||
self.assertEqual(len(set(addrs)), len(addrs))
|
|
||||||
for addr in addrs:
|
|
||||||
self.assertIsInstance(addr.family, int)
|
|
||||||
self.assertIsInstance(addr.address, str)
|
|
||||||
self.assertIsInstance(addr.netmask, (str, type(None)))
|
|
||||||
self.assertIsInstance(addr.broadcast, (str, type(None)))
|
|
||||||
self.assertIn(addr.family, families)
|
|
||||||
if sys.version_info >= (3, 4):
|
|
||||||
self.assertIsInstance(addr.family, enum.IntEnum)
|
|
||||||
if nic_stats[nic].isup:
|
|
||||||
# Do not test binding to addresses of interfaces
|
|
||||||
# that are down
|
|
||||||
if addr.family == socket.AF_INET:
|
|
||||||
s = socket.socket(addr.family)
|
|
||||||
with contextlib.closing(s):
|
|
||||||
s.bind((addr.address, 0))
|
|
||||||
elif addr.family == socket.AF_INET6:
|
|
||||||
info = socket.getaddrinfo(
|
|
||||||
addr.address, 0, socket.AF_INET6,
|
|
||||||
socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0]
|
|
||||||
af, socktype, proto, canonname, sa = info
|
|
||||||
s = socket.socket(af, socktype, proto)
|
|
||||||
with contextlib.closing(s):
|
|
||||||
s.bind(sa)
|
|
||||||
for ip in (addr.address, addr.netmask, addr.broadcast,
|
|
||||||
addr.ptp):
|
|
||||||
if ip is not None:
|
|
||||||
# TODO: skip AF_INET6 for now because I get:
|
|
||||||
# AddressValueError: Only hex digits permitted in
|
|
||||||
# u'c6f3%lxcbr0' in u'fe80::c8e0:fff:fe54:c6f3%lxcbr0'
|
|
||||||
if addr.family != socket.AF_INET6:
|
|
||||||
check_net_address(ip, addr.family)
|
|
||||||
# broadcast and ptp addresses are mutually exclusive
|
|
||||||
if addr.broadcast:
|
|
||||||
self.assertIsNone(addr.ptp)
|
|
||||||
elif addr.ptp:
|
|
||||||
self.assertIsNone(addr.broadcast)
|
|
||||||
|
|
||||||
if BSD or MACOS or SUNOS:
|
|
||||||
if hasattr(socket, "AF_LINK"):
|
|
||||||
self.assertEqual(psutil.AF_LINK, socket.AF_LINK)
|
|
||||||
elif LINUX:
|
|
||||||
self.assertEqual(psutil.AF_LINK, socket.AF_PACKET)
|
|
||||||
elif WINDOWS:
|
|
||||||
self.assertEqual(psutil.AF_LINK, -1)
|
|
||||||
|
|
||||||
def test_net_if_addrs_mac_null_bytes(self):
|
|
||||||
# Simulate that the underlying C function returns an incomplete
|
|
||||||
# MAC address. psutil is supposed to fill it with null bytes.
|
|
||||||
# https://github.com/giampaolo/psutil/issues/786
|
|
||||||
if POSIX:
|
|
||||||
ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)]
|
|
||||||
else:
|
|
||||||
ret = [('em1', -1, '06-3d-29', None, None, None)]
|
|
||||||
with mock.patch('psutil._psplatform.net_if_addrs',
|
|
||||||
return_value=ret) as m:
|
|
||||||
addr = psutil.net_if_addrs()['em1'][0]
|
|
||||||
assert m.called
|
|
||||||
if POSIX:
|
|
||||||
self.assertEqual(addr.address, '06:3d:29:00:00:00')
|
|
||||||
else:
|
|
||||||
self.assertEqual(addr.address, '06-3d-29-00-00-00')
|
|
||||||
|
|
||||||
@unittest.skipIf(TRAVIS, "unreliable on TRAVIS") # raises EPERM
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
nics = psutil.net_if_stats()
|
|
||||||
assert nics, nics
|
|
||||||
all_duplexes = (psutil.NIC_DUPLEX_FULL,
|
|
||||||
psutil.NIC_DUPLEX_HALF,
|
|
||||||
psutil.NIC_DUPLEX_UNKNOWN)
|
|
||||||
for name, stats in nics.items():
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
isup, duplex, speed, mtu = stats
|
|
||||||
self.assertIsInstance(isup, bool)
|
|
||||||
self.assertIn(duplex, all_duplexes)
|
|
||||||
self.assertIn(duplex, all_duplexes)
|
|
||||||
self.assertGreaterEqual(speed, 0)
|
|
||||||
self.assertGreaterEqual(mtu, 0)
|
|
||||||
|
|
||||||
@unittest.skipIf(not (LINUX or BSD or MACOS),
|
|
||||||
"LINUX or BSD or MACOS specific")
|
|
||||||
def test_net_if_stats_enodev(self):
|
|
||||||
# See: https://github.com/giampaolo/psutil/issues/1279
|
|
||||||
with mock.patch('psutil._psutil_posix.net_if_mtu',
|
|
||||||
side_effect=OSError(errno.ENODEV, "")) as m:
|
|
||||||
ret = psutil.net_if_stats()
|
|
||||||
self.assertEqual(ret, {})
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
|
|
||||||
'/proc/diskstats not available on this linux version')
|
|
||||||
@unittest.skipIf(APPVEYOR and psutil.disk_io_counters() is None,
|
|
||||||
"unreliable on APPVEYOR") # no visible disks
|
|
||||||
def test_disk_io_counters(self):
|
|
||||||
def check_ntuple(nt):
|
|
||||||
self.assertEqual(nt[0], nt.read_count)
|
|
||||||
self.assertEqual(nt[1], nt.write_count)
|
|
||||||
self.assertEqual(nt[2], nt.read_bytes)
|
|
||||||
self.assertEqual(nt[3], nt.write_bytes)
|
|
||||||
if not (OPENBSD or NETBSD):
|
|
||||||
self.assertEqual(nt[4], nt.read_time)
|
|
||||||
self.assertEqual(nt[5], nt.write_time)
|
|
||||||
if LINUX:
|
|
||||||
self.assertEqual(nt[6], nt.read_merged_count)
|
|
||||||
self.assertEqual(nt[7], nt.write_merged_count)
|
|
||||||
self.assertEqual(nt[8], nt.busy_time)
|
|
||||||
elif FREEBSD:
|
|
||||||
self.assertEqual(nt[6], nt.busy_time)
|
|
||||||
for name in nt._fields:
|
|
||||||
assert getattr(nt, name) >= 0, nt
|
|
||||||
|
|
||||||
ret = psutil.disk_io_counters(perdisk=False)
|
|
||||||
assert ret is not None, "no disks on this system?"
|
|
||||||
check_ntuple(ret)
|
|
||||||
ret = psutil.disk_io_counters(perdisk=True)
|
|
||||||
# make sure there are no duplicates
|
|
||||||
self.assertEqual(len(ret), len(set(ret)))
|
|
||||||
for key in ret:
|
|
||||||
assert key, key
|
|
||||||
check_ntuple(ret[key])
|
|
||||||
|
|
||||||
def test_disk_io_counters_no_disks(self):
|
|
||||||
# Emulate a case where no disks are installed, see:
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1062
|
|
||||||
with mock.patch('psutil._psplatform.disk_io_counters',
|
|
||||||
return_value={}) as m:
|
|
||||||
self.assertIsNone(psutil.disk_io_counters(perdisk=False))
|
|
||||||
self.assertEqual(psutil.disk_io_counters(perdisk=True), {})
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
# can't find users on APPVEYOR or TRAVIS
|
|
||||||
@unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(),
|
|
||||||
"unreliable on APPVEYOR or TRAVIS")
|
|
||||||
def test_users(self):
|
|
||||||
users = psutil.users()
|
|
||||||
self.assertNotEqual(users, [])
|
|
||||||
for user in users:
|
|
||||||
assert user.name, user
|
|
||||||
self.assertIsInstance(user.name, str)
|
|
||||||
self.assertIsInstance(user.terminal, (str, type(None)))
|
|
||||||
if user.host is not None:
|
|
||||||
self.assertIsInstance(user.host, (str, type(None)))
|
|
||||||
user.terminal
|
|
||||||
user.host
|
|
||||||
assert user.started > 0.0, user
|
|
||||||
datetime.datetime.fromtimestamp(user.started)
|
|
||||||
if WINDOWS or OPENBSD:
|
|
||||||
self.assertIsNone(user.pid)
|
|
||||||
else:
|
|
||||||
psutil.Process(user.pid)
|
|
||||||
|
|
||||||
def test_cpu_stats(self):
|
|
||||||
# Tested more extensively in per-platform test modules.
|
|
||||||
infos = psutil.cpu_stats()
|
|
||||||
self.assertEqual(
|
|
||||||
infos._fields,
|
|
||||||
('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'))
|
|
||||||
for name in infos._fields:
|
|
||||||
value = getattr(infos, name)
|
|
||||||
self.assertGreaterEqual(value, 0)
|
|
||||||
# on AIX, ctx_switches is always 0
|
|
||||||
if not AIX and name in ('ctx_switches', 'interrupts'):
|
|
||||||
self.assertGreater(value, 0)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_CPU_FREQ, "not suported")
|
|
||||||
def test_cpu_freq(self):
|
|
||||||
def check_ls(ls):
|
|
||||||
for nt in ls:
|
|
||||||
self.assertEqual(nt._fields, ('current', 'min', 'max'))
|
|
||||||
self.assertLessEqual(nt.current, nt.max)
|
|
||||||
for name in nt._fields:
|
|
||||||
value = getattr(nt, name)
|
|
||||||
self.assertIsInstance(value, (int, long, float))
|
|
||||||
self.assertGreaterEqual(value, 0)
|
|
||||||
|
|
||||||
ls = psutil.cpu_freq(percpu=True)
|
|
||||||
if TRAVIS and not ls:
|
|
||||||
return
|
|
||||||
|
|
||||||
assert ls, ls
|
|
||||||
check_ls([psutil.cpu_freq(percpu=False)])
|
|
||||||
|
|
||||||
if LINUX:
|
|
||||||
self.assertEqual(len(ls), psutil.cpu_count())
|
|
||||||
|
|
||||||
def test_os_constants(self):
|
|
||||||
names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD",
|
|
||||||
"NETBSD", "BSD", "SUNOS"]
|
|
||||||
for name in names:
|
|
||||||
self.assertIsInstance(getattr(psutil, name), bool, msg=name)
|
|
||||||
|
|
||||||
if os.name == 'posix':
|
|
||||||
assert psutil.POSIX
|
|
||||||
assert not psutil.WINDOWS
|
|
||||||
names.remove("POSIX")
|
|
||||||
if "linux" in sys.platform.lower():
|
|
||||||
assert psutil.LINUX
|
|
||||||
names.remove("LINUX")
|
|
||||||
elif "bsd" in sys.platform.lower():
|
|
||||||
assert psutil.BSD
|
|
||||||
self.assertEqual([psutil.FREEBSD, psutil.OPENBSD,
|
|
||||||
psutil.NETBSD].count(True), 1)
|
|
||||||
names.remove("BSD")
|
|
||||||
names.remove("FREEBSD")
|
|
||||||
names.remove("OPENBSD")
|
|
||||||
names.remove("NETBSD")
|
|
||||||
elif "sunos" in sys.platform.lower() or \
|
|
||||||
"solaris" in sys.platform.lower():
|
|
||||||
assert psutil.SUNOS
|
|
||||||
names.remove("SUNOS")
|
|
||||||
elif "darwin" in sys.platform.lower():
|
|
||||||
assert psutil.MACOS
|
|
||||||
names.remove("MACOS")
|
|
||||||
else:
|
|
||||||
assert psutil.WINDOWS
|
|
||||||
assert not psutil.POSIX
|
|
||||||
names.remove("WINDOWS")
|
|
||||||
|
|
||||||
# assert all other constants are set to False
|
|
||||||
for name in names:
|
|
||||||
self.assertIs(getattr(psutil, name), False, msg=name)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
|
|
||||||
def test_sensors_temperatures(self):
|
|
||||||
temps = psutil.sensors_temperatures()
|
|
||||||
for name, entries in temps.items():
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
for entry in entries:
|
|
||||||
self.assertIsInstance(entry.label, str)
|
|
||||||
if entry.current is not None:
|
|
||||||
self.assertGreaterEqual(entry.current, 0)
|
|
||||||
if entry.high is not None:
|
|
||||||
self.assertGreaterEqual(entry.high, 0)
|
|
||||||
if entry.critical is not None:
|
|
||||||
self.assertGreaterEqual(entry.critical, 0)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported")
|
|
||||||
def test_sensors_temperatures_fahreneit(self):
|
|
||||||
d = {'coretemp': [('label', 50.0, 60.0, 70.0)]}
|
|
||||||
with mock.patch("psutil._psplatform.sensors_temperatures",
|
|
||||||
return_value=d) as m:
|
|
||||||
temps = psutil.sensors_temperatures(
|
|
||||||
fahrenheit=True)['coretemp'][0]
|
|
||||||
assert m.called
|
|
||||||
self.assertEqual(temps.current, 122.0)
|
|
||||||
self.assertEqual(temps.high, 140.0)
|
|
||||||
self.assertEqual(temps.critical, 158.0)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_BATTERY, "not supported")
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_sensors_battery(self):
|
|
||||||
ret = psutil.sensors_battery()
|
|
||||||
self.assertGreaterEqual(ret.percent, 0)
|
|
||||||
self.assertLessEqual(ret.percent, 100)
|
|
||||||
if ret.secsleft not in (psutil.POWER_TIME_UNKNOWN,
|
|
||||||
psutil.POWER_TIME_UNLIMITED):
|
|
||||||
self.assertGreaterEqual(ret.secsleft, 0)
|
|
||||||
else:
|
|
||||||
if ret.secsleft == psutil.POWER_TIME_UNLIMITED:
|
|
||||||
self.assertTrue(ret.power_plugged)
|
|
||||||
self.assertIsInstance(ret.power_plugged, bool)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_SENSORS_FANS, "not supported")
|
|
||||||
def test_sensors_fans(self):
|
|
||||||
fans = psutil.sensors_fans()
|
|
||||||
for name, entries in fans.items():
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
for entry in entries:
|
|
||||||
self.assertIsInstance(entry.label, str)
|
|
||||||
self.assertIsInstance(entry.current, (int, long))
|
|
||||||
self.assertGreaterEqual(entry.current, 0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,366 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Notes about unicode handling in psutil
|
|
||||||
======================================
|
|
||||||
|
|
||||||
In psutil these are the APIs returning or dealing with a string
|
|
||||||
('not tested' means they are not tested to deal with non-ASCII strings):
|
|
||||||
|
|
||||||
* Process.cmdline()
|
|
||||||
* Process.connections('unix')
|
|
||||||
* Process.cwd()
|
|
||||||
* Process.environ()
|
|
||||||
* Process.exe()
|
|
||||||
* Process.memory_maps()
|
|
||||||
* Process.name()
|
|
||||||
* Process.open_files()
|
|
||||||
* Process.username() (not tested)
|
|
||||||
|
|
||||||
* disk_io_counters() (not tested)
|
|
||||||
* disk_partitions() (not tested)
|
|
||||||
* disk_usage(str)
|
|
||||||
* net_connections('unix')
|
|
||||||
* net_if_addrs() (not tested)
|
|
||||||
* net_if_stats() (not tested)
|
|
||||||
* net_io_counters() (not tested)
|
|
||||||
* sensors_fans() (not tested)
|
|
||||||
* sensors_temperatures() (not tested)
|
|
||||||
* users() (not tested)
|
|
||||||
|
|
||||||
* WindowsService.binpath() (not tested)
|
|
||||||
* WindowsService.description() (not tested)
|
|
||||||
* WindowsService.display_name() (not tested)
|
|
||||||
* WindowsService.name() (not tested)
|
|
||||||
* WindowsService.status() (not tested)
|
|
||||||
* WindowsService.username() (not tested)
|
|
||||||
|
|
||||||
In here we create a unicode path with a funky non-ASCII name and (where
|
|
||||||
possible) make psutil return it back (e.g. on name(), exe(), open_files(),
|
|
||||||
etc.) and make sure that:
|
|
||||||
|
|
||||||
* psutil never crashes with UnicodeDecodeError
|
|
||||||
* the returned path matches
|
|
||||||
|
|
||||||
For a detailed explanation of how psutil handles unicode see:
|
|
||||||
- https://github.com/giampaolo/psutil/issues/1040
|
|
||||||
- http://psutil.readthedocs.io/#unicode
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import traceback
|
|
||||||
import warnings
|
|
||||||
from contextlib import closing
|
|
||||||
|
|
||||||
from psutil import BSD
|
|
||||||
from psutil import MACOS
|
|
||||||
from psutil import OPENBSD
|
|
||||||
from psutil import POSIX
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil._compat import PY3
|
|
||||||
from psutil._compat import u
|
|
||||||
from psutil.tests import APPVEYOR
|
|
||||||
from psutil.tests import ASCII_FS
|
|
||||||
from psutil.tests import bind_unix_socket
|
|
||||||
from psutil.tests import chdir
|
|
||||||
from psutil.tests import copyload_shared_lib
|
|
||||||
from psutil.tests import create_exe
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_CONNECTIONS_UNIX
|
|
||||||
from psutil.tests import HAS_ENVIRON
|
|
||||||
from psutil.tests import HAS_MEMORY_MAPS
|
|
||||||
from psutil.tests import mock
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import safe_mkdir
|
|
||||||
from psutil.tests import safe_rmpath as _safe_rmpath
|
|
||||||
from psutil.tests import skip_on_access_denied
|
|
||||||
from psutil.tests import TESTFILE_PREFIX
|
|
||||||
from psutil.tests import TESTFN
|
|
||||||
from psutil.tests import TESTFN_UNICODE
|
|
||||||
from psutil.tests import TRAVIS
|
|
||||||
from psutil.tests import unittest
|
|
||||||
from psutil.tests import unix_socket_path
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
|
|
||||||
def safe_rmpath(path):
|
|
||||||
if APPVEYOR:
|
|
||||||
# TODO - this is quite random and I'm not sure why it happens,
|
|
||||||
# nor I can reproduce it locally:
|
|
||||||
# https://ci.appveyor.com/project/giampaolo/psutil/build/job/
|
|
||||||
# jiq2cgd6stsbtn60
|
|
||||||
# safe_rmpath() happens after reap_children() so this is weird
|
|
||||||
# Perhaps wait_procs() on Windows is broken? Maybe because
|
|
||||||
# of STILL_ACTIVE?
|
|
||||||
# https://github.com/giampaolo/psutil/blob/
|
|
||||||
# 68c7a70728a31d8b8b58f4be6c4c0baa2f449eda/psutil/arch/
|
|
||||||
# windows/process_info.c#L146
|
|
||||||
try:
|
|
||||||
return _safe_rmpath(path)
|
|
||||||
except WindowsError:
|
|
||||||
traceback.print_exc()
|
|
||||||
else:
|
|
||||||
return _safe_rmpath(path)
|
|
||||||
|
|
||||||
|
|
||||||
def subprocess_supports_unicode(name):
|
|
||||||
"""Return True if both the fs and the subprocess module can
|
|
||||||
deal with a unicode file name.
|
|
||||||
"""
|
|
||||||
if PY3:
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
safe_rmpath(name)
|
|
||||||
create_exe(name)
|
|
||||||
get_test_subprocess(cmd=[name])
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
finally:
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
|
|
||||||
# An invalid unicode string.
|
|
||||||
if PY3:
|
|
||||||
INVALID_NAME = (TESTFN.encode('utf8') + b"f\xc0\x80").decode(
|
|
||||||
'utf8', 'surrogateescape')
|
|
||||||
else:
|
|
||||||
INVALID_NAME = TESTFN + "f\xc0\x80"
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# FS APIs
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class _BaseFSAPIsTests(object):
|
|
||||||
funky_name = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
safe_rmpath(cls.funky_name)
|
|
||||||
create_exe(cls.funky_name)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
safe_rmpath(cls.funky_name)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def expect_exact_path_match(self):
|
|
||||||
raise NotImplementedError("must be implemented in subclass")
|
|
||||||
|
|
||||||
def test_proc_exe(self):
|
|
||||||
subp = get_test_subprocess(cmd=[self.funky_name])
|
|
||||||
p = psutil.Process(subp.pid)
|
|
||||||
exe = p.exe()
|
|
||||||
self.assertIsInstance(exe, str)
|
|
||||||
if self.expect_exact_path_match():
|
|
||||||
self.assertEqual(exe, self.funky_name)
|
|
||||||
|
|
||||||
def test_proc_name(self):
|
|
||||||
subp = get_test_subprocess(cmd=[self.funky_name])
|
|
||||||
if WINDOWS:
|
|
||||||
# On Windows name() is determined from exe() first, because
|
|
||||||
# it's faster; we want to overcome the internal optimization
|
|
||||||
# and test name() instead of exe().
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_exe",
|
|
||||||
side_effect=psutil.AccessDenied(os.getpid())) as m:
|
|
||||||
name = psutil.Process(subp.pid).name()
|
|
||||||
assert m.called
|
|
||||||
else:
|
|
||||||
name = psutil.Process(subp.pid).name()
|
|
||||||
self.assertIsInstance(name, str)
|
|
||||||
if self.expect_exact_path_match():
|
|
||||||
self.assertEqual(name, os.path.basename(self.funky_name))
|
|
||||||
|
|
||||||
def test_proc_cmdline(self):
|
|
||||||
subp = get_test_subprocess(cmd=[self.funky_name])
|
|
||||||
p = psutil.Process(subp.pid)
|
|
||||||
cmdline = p.cmdline()
|
|
||||||
for part in cmdline:
|
|
||||||
self.assertIsInstance(part, str)
|
|
||||||
if self.expect_exact_path_match():
|
|
||||||
self.assertEqual(cmdline, [self.funky_name])
|
|
||||||
|
|
||||||
def test_proc_cwd(self):
|
|
||||||
dname = self.funky_name + "2"
|
|
||||||
self.addCleanup(safe_rmpath, dname)
|
|
||||||
safe_mkdir(dname)
|
|
||||||
with chdir(dname):
|
|
||||||
p = psutil.Process()
|
|
||||||
cwd = p.cwd()
|
|
||||||
self.assertIsInstance(p.cwd(), str)
|
|
||||||
if self.expect_exact_path_match():
|
|
||||||
self.assertEqual(cwd, dname)
|
|
||||||
|
|
||||||
def test_proc_open_files(self):
|
|
||||||
p = psutil.Process()
|
|
||||||
start = set(p.open_files())
|
|
||||||
with open(self.funky_name, 'rb'):
|
|
||||||
new = set(p.open_files())
|
|
||||||
path = (new - start).pop().path
|
|
||||||
self.assertIsInstance(path, str)
|
|
||||||
if BSD and not path:
|
|
||||||
# XXX - see https://github.com/giampaolo/psutil/issues/595
|
|
||||||
return self.skipTest("open_files on BSD is broken")
|
|
||||||
if self.expect_exact_path_match():
|
|
||||||
self.assertEqual(os.path.normcase(path),
|
|
||||||
os.path.normcase(self.funky_name))
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
def test_proc_connections(self):
|
|
||||||
suffix = os.path.basename(self.funky_name)
|
|
||||||
with unix_socket_path(suffix=suffix) as name:
|
|
||||||
try:
|
|
||||||
sock = bind_unix_socket(name)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
if PY3:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
raise unittest.SkipTest("not supported")
|
|
||||||
with closing(sock):
|
|
||||||
conn = psutil.Process().connections('unix')[0]
|
|
||||||
self.assertIsInstance(conn.laddr, str)
|
|
||||||
# AF_UNIX addr not set on OpenBSD
|
|
||||||
if not OPENBSD:
|
|
||||||
self.assertEqual(conn.laddr, name)
|
|
||||||
|
|
||||||
@unittest.skipIf(not POSIX, "POSIX only")
|
|
||||||
@unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets")
|
|
||||||
@skip_on_access_denied()
|
|
||||||
def test_net_connections(self):
|
|
||||||
def find_sock(cons):
|
|
||||||
for conn in cons:
|
|
||||||
if os.path.basename(conn.laddr).startswith(TESTFILE_PREFIX):
|
|
||||||
return conn
|
|
||||||
raise ValueError("connection not found")
|
|
||||||
|
|
||||||
suffix = os.path.basename(self.funky_name)
|
|
||||||
with unix_socket_path(suffix=suffix) as name:
|
|
||||||
try:
|
|
||||||
sock = bind_unix_socket(name)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
if PY3:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
raise unittest.SkipTest("not supported")
|
|
||||||
with closing(sock):
|
|
||||||
cons = psutil.net_connections(kind='unix')
|
|
||||||
# AF_UNIX addr not set on OpenBSD
|
|
||||||
if not OPENBSD:
|
|
||||||
conn = find_sock(cons)
|
|
||||||
self.assertIsInstance(conn.laddr, str)
|
|
||||||
self.assertEqual(conn.laddr, name)
|
|
||||||
|
|
||||||
def test_disk_usage(self):
|
|
||||||
dname = self.funky_name + "2"
|
|
||||||
self.addCleanup(safe_rmpath, dname)
|
|
||||||
safe_mkdir(dname)
|
|
||||||
psutil.disk_usage(dname)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
|
|
||||||
@unittest.skipIf(not PY3, "ctypes does not support unicode on PY2")
|
|
||||||
def test_memory_maps(self):
|
|
||||||
# XXX: on Python 2, using ctypes.CDLL with a unicode path
|
|
||||||
# opens a message box which blocks the test run.
|
|
||||||
with copyload_shared_lib(dst_prefix=self.funky_name) as funky_path:
|
|
||||||
def normpath(p):
|
|
||||||
return os.path.realpath(os.path.normcase(p))
|
|
||||||
libpaths = [normpath(x.path)
|
|
||||||
for x in psutil.Process().memory_maps()]
|
|
||||||
# ...just to have a clearer msg in case of failure
|
|
||||||
libpaths = [x for x in libpaths if TESTFILE_PREFIX in x]
|
|
||||||
self.assertIn(normpath(funky_path), libpaths)
|
|
||||||
for path in libpaths:
|
|
||||||
self.assertIsInstance(path, str)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO
|
|
||||||
@unittest.skipIf(ASCII_FS, "ASCII fs")
|
|
||||||
@unittest.skipIf(not subprocess_supports_unicode(TESTFN_UNICODE),
|
|
||||||
"subprocess can't deal with unicode")
|
|
||||||
class TestFSAPIs(_BaseFSAPIsTests, unittest.TestCase):
|
|
||||||
"""Test FS APIs with a funky, valid, UTF8 path name."""
|
|
||||||
funky_name = TESTFN_UNICODE
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def expect_exact_path_match(cls):
|
|
||||||
# Do not expect psutil to correctly handle unicode paths on
|
|
||||||
# Python 2 if os.listdir() is not able either.
|
|
||||||
if PY3:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
here = '.' if isinstance(cls.funky_name, str) else u('.')
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
return cls.funky_name in os.listdir(here)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(MACOS and TRAVIS, "unreliable on TRAVIS") # TODO
|
|
||||||
@unittest.skipIf(not subprocess_supports_unicode(INVALID_NAME),
|
|
||||||
"subprocess can't deal with invalid unicode")
|
|
||||||
class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase):
|
|
||||||
"""Test FS APIs with a funky, invalid path name."""
|
|
||||||
funky_name = INVALID_NAME
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def expect_exact_path_match(cls):
|
|
||||||
# Invalid unicode names are supposed to work on Python 2.
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestWinProcessName(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_name_type(self):
|
|
||||||
# On Windows name() is determined from exe() first, because
|
|
||||||
# it's faster; we want to overcome the internal optimization
|
|
||||||
# and test name() instead of exe().
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_exe",
|
|
||||||
side_effect=psutil.AccessDenied(os.getpid())) as m:
|
|
||||||
self.assertIsInstance(psutil.Process().name(), str)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# Non fs APIs
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
class TestNonFSAPIS(unittest.TestCase):
|
|
||||||
"""Unicode tests for non fs-related APIs."""
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_ENVIRON, "not supported")
|
|
||||||
def test_proc_environ(self):
|
|
||||||
# Note: differently from others, this test does not deal
|
|
||||||
# with fs paths. On Python 2 subprocess module is broken as
|
|
||||||
# it's not able to handle with non-ASCII env vars, so
|
|
||||||
# we use "è", which is part of the extended ASCII table
|
|
||||||
# (unicode point <= 255).
|
|
||||||
env = os.environ.copy()
|
|
||||||
funky_str = TESTFN_UNICODE if PY3 else 'è'
|
|
||||||
env['FUNNY_ARG'] = funky_str
|
|
||||||
sproc = get_test_subprocess(env=env)
|
|
||||||
p = psutil.Process(sproc.pid)
|
|
||||||
env = p.environ()
|
|
||||||
for k, v in env.items():
|
|
||||||
self.assertIsInstance(k, str)
|
|
||||||
self.assertIsInstance(v, str)
|
|
||||||
self.assertEqual(env['FUNNY_ARG'], funky_str)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
|
@ -1,859 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: UTF-8 -*
|
|
||||||
|
|
||||||
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""Windows specific tests."""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import errno
|
|
||||||
import glob
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import re
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
from psutil import WINDOWS
|
|
||||||
from psutil.tests import APPVEYOR
|
|
||||||
from psutil.tests import get_test_subprocess
|
|
||||||
from psutil.tests import HAS_BATTERY
|
|
||||||
from psutil.tests import mock
|
|
||||||
from psutil.tests import reap_children
|
|
||||||
from psutil.tests import retry_before_failing
|
|
||||||
from psutil.tests import run_test_module_by_name
|
|
||||||
from psutil.tests import sh
|
|
||||||
from psutil.tests import unittest
|
|
||||||
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("ignore")
|
|
||||||
try:
|
|
||||||
import win32api # requires "pip install pypiwin32"
|
|
||||||
import win32con
|
|
||||||
import win32process
|
|
||||||
import wmi # requires "pip install wmi" / "make setup-dev-env"
|
|
||||||
except ImportError:
|
|
||||||
if os.name == 'nt':
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
cext = psutil._psplatform.cext
|
|
||||||
|
|
||||||
# are we a 64 bit process
|
|
||||||
IS_64_BIT = sys.maxsize > 2**32
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_exceptions(fun):
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
return fun(self, *args, **kwargs)
|
|
||||||
except OSError as err:
|
|
||||||
from psutil._pswindows import ACCESS_DENIED_SET
|
|
||||||
if err.errno in ACCESS_DENIED_SET:
|
|
||||||
raise psutil.AccessDenied(None, None)
|
|
||||||
if err.errno == errno.ESRCH:
|
|
||||||
raise psutil.NoSuchProcess(None, None)
|
|
||||||
raise
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# System APIs
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestCpuAPIs(unittest.TestCase):
|
|
||||||
|
|
||||||
@unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ,
|
|
||||||
'NUMBER_OF_PROCESSORS env var is not available')
|
|
||||||
def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self):
|
|
||||||
# Will likely fail on many-cores systems:
|
|
||||||
# https://stackoverflow.com/questions/31209256
|
|
||||||
num_cpus = int(os.environ['NUMBER_OF_PROCESSORS'])
|
|
||||||
self.assertEqual(num_cpus, psutil.cpu_count())
|
|
||||||
|
|
||||||
def test_cpu_count_vs_GetSystemInfo(self):
|
|
||||||
# Will likely fail on many-cores systems:
|
|
||||||
# https://stackoverflow.com/questions/31209256
|
|
||||||
sys_value = win32api.GetSystemInfo()[5]
|
|
||||||
psutil_value = psutil.cpu_count()
|
|
||||||
self.assertEqual(sys_value, psutil_value)
|
|
||||||
|
|
||||||
def test_cpu_count_logical_vs_wmi(self):
|
|
||||||
w = wmi.WMI()
|
|
||||||
proc = w.Win32_Processor()[0]
|
|
||||||
self.assertEqual(psutil.cpu_count(), proc.NumberOfLogicalProcessors)
|
|
||||||
|
|
||||||
def test_cpu_count_phys_vs_wmi(self):
|
|
||||||
w = wmi.WMI()
|
|
||||||
proc = w.Win32_Processor()[0]
|
|
||||||
self.assertEqual(psutil.cpu_count(logical=False), proc.NumberOfCores)
|
|
||||||
|
|
||||||
def test_cpu_count_vs_cpu_times(self):
|
|
||||||
self.assertEqual(psutil.cpu_count(),
|
|
||||||
len(psutil.cpu_times(percpu=True)))
|
|
||||||
|
|
||||||
def test_cpu_freq(self):
|
|
||||||
w = wmi.WMI()
|
|
||||||
proc = w.Win32_Processor()[0]
|
|
||||||
self.assertEqual(proc.CurrentClockSpeed, psutil.cpu_freq().current)
|
|
||||||
self.assertEqual(proc.MaxClockSpeed, psutil.cpu_freq().max)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestSystemAPIs(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_nic_names(self):
|
|
||||||
out = sh('ipconfig /all')
|
|
||||||
nics = psutil.net_io_counters(pernic=True).keys()
|
|
||||||
for nic in nics:
|
|
||||||
if "pseudo-interface" in nic.replace(' ', '-').lower():
|
|
||||||
continue
|
|
||||||
if nic not in out:
|
|
||||||
self.fail(
|
|
||||||
"%r nic wasn't found in 'ipconfig /all' output" % nic)
|
|
||||||
|
|
||||||
def test_total_phymem(self):
|
|
||||||
w = wmi.WMI().Win32_ComputerSystem()[0]
|
|
||||||
self.assertEqual(int(w.TotalPhysicalMemory),
|
|
||||||
psutil.virtual_memory().total)
|
|
||||||
|
|
||||||
# @unittest.skipIf(wmi is None, "wmi module is not installed")
|
|
||||||
# def test__UPTIME(self):
|
|
||||||
# # _UPTIME constant is not public but it is used internally
|
|
||||||
# # as value to return for pid 0 creation time.
|
|
||||||
# # WMI behaves the same.
|
|
||||||
# w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
# p = psutil.Process(0)
|
|
||||||
# wmic_create = str(w.CreationDate.split('.')[0])
|
|
||||||
# psutil_create = time.strftime("%Y%m%d%H%M%S",
|
|
||||||
# time.localtime(p.create_time()))
|
|
||||||
|
|
||||||
# Note: this test is not very reliable
|
|
||||||
@unittest.skipIf(APPVEYOR, "test not relieable on appveyor")
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_pids(self):
|
|
||||||
# Note: this test might fail if the OS is starting/killing
|
|
||||||
# other processes in the meantime
|
|
||||||
w = wmi.WMI().Win32_Process()
|
|
||||||
wmi_pids = set([x.ProcessId for x in w])
|
|
||||||
psutil_pids = set(psutil.pids())
|
|
||||||
self.assertEqual(wmi_pids, psutil_pids)
|
|
||||||
|
|
||||||
@retry_before_failing()
|
|
||||||
def test_disks(self):
|
|
||||||
ps_parts = psutil.disk_partitions(all=True)
|
|
||||||
wmi_parts = wmi.WMI().Win32_LogicalDisk()
|
|
||||||
for ps_part in ps_parts:
|
|
||||||
for wmi_part in wmi_parts:
|
|
||||||
if ps_part.device.replace('\\', '') == wmi_part.DeviceID:
|
|
||||||
if not ps_part.mountpoint:
|
|
||||||
# this is usually a CD-ROM with no disk inserted
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
usage = psutil.disk_usage(ps_part.mountpoint)
|
|
||||||
except OSError as err:
|
|
||||||
if err.errno == errno.ENOENT:
|
|
||||||
# usually this is the floppy
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
self.assertEqual(usage.total, int(wmi_part.Size))
|
|
||||||
wmi_free = int(wmi_part.FreeSpace)
|
|
||||||
self.assertEqual(usage.free, wmi_free)
|
|
||||||
# 10 MB tollerance
|
|
||||||
if abs(usage.free - wmi_free) > 10 * 1024 * 1024:
|
|
||||||
self.fail("psutil=%s, wmi=%s" % (
|
|
||||||
usage.free, wmi_free))
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.fail("can't find partition %s" % repr(ps_part))
|
|
||||||
|
|
||||||
def test_disk_usage(self):
|
|
||||||
for disk in psutil.disk_partitions():
|
|
||||||
sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint)
|
|
||||||
psutil_value = psutil.disk_usage(disk.mountpoint)
|
|
||||||
self.assertAlmostEqual(sys_value[0], psutil_value.free,
|
|
||||||
delta=1024 * 1024)
|
|
||||||
self.assertAlmostEqual(sys_value[1], psutil_value.total,
|
|
||||||
delta=1024 * 1024)
|
|
||||||
self.assertEqual(psutil_value.used,
|
|
||||||
psutil_value.total - psutil_value.free)
|
|
||||||
|
|
||||||
def test_disk_partitions(self):
|
|
||||||
sys_value = [
|
|
||||||
x + '\\' for x in win32api.GetLogicalDriveStrings().split("\\\x00")
|
|
||||||
if x and not x.startswith('A:')]
|
|
||||||
psutil_value = [x.mountpoint for x in psutil.disk_partitions(all=True)]
|
|
||||||
self.assertEqual(sys_value, psutil_value)
|
|
||||||
|
|
||||||
def test_net_if_stats(self):
|
|
||||||
ps_names = set(cext.net_if_stats())
|
|
||||||
wmi_adapters = wmi.WMI().Win32_NetworkAdapter()
|
|
||||||
wmi_names = set()
|
|
||||||
for wmi_adapter in wmi_adapters:
|
|
||||||
wmi_names.add(wmi_adapter.Name)
|
|
||||||
wmi_names.add(wmi_adapter.NetConnectionID)
|
|
||||||
self.assertTrue(ps_names & wmi_names,
|
|
||||||
"no common entries in %s, %s" % (ps_names, wmi_names))
|
|
||||||
|
|
||||||
def test_boot_time(self):
|
|
||||||
wmi_os = wmi.WMI().Win32_OperatingSystem()
|
|
||||||
wmi_btime_str = wmi_os[0].LastBootUpTime.split('.')[0]
|
|
||||||
wmi_btime_dt = datetime.datetime.strptime(
|
|
||||||
wmi_btime_str, "%Y%m%d%H%M%S")
|
|
||||||
psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time())
|
|
||||||
diff = abs((wmi_btime_dt - psutil_dt).total_seconds())
|
|
||||||
# Wmic time is 2-3 secs lower for some reason; that's OK.
|
|
||||||
self.assertLessEqual(diff, 3)
|
|
||||||
|
|
||||||
def test_boot_time_fluctuation(self):
|
|
||||||
# https://github.com/giampaolo/psutil/issues/1007
|
|
||||||
with mock.patch('psutil._pswindows.cext.boot_time', return_value=5):
|
|
||||||
self.assertEqual(psutil.boot_time(), 5)
|
|
||||||
with mock.patch('psutil._pswindows.cext.boot_time', return_value=4):
|
|
||||||
self.assertEqual(psutil.boot_time(), 5)
|
|
||||||
with mock.patch('psutil._pswindows.cext.boot_time', return_value=6):
|
|
||||||
self.assertEqual(psutil.boot_time(), 5)
|
|
||||||
with mock.patch('psutil._pswindows.cext.boot_time', return_value=333):
|
|
||||||
self.assertEqual(psutil.boot_time(), 333)
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# sensors_battery()
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestSensorsBattery(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_has_battery(self):
|
|
||||||
if win32api.GetPwrCapabilities()['SystemBatteriesPresent']:
|
|
||||||
self.assertIsNotNone(psutil.sensors_battery())
|
|
||||||
else:
|
|
||||||
self.assertIsNone(psutil.sensors_battery())
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_percent(self):
|
|
||||||
w = wmi.WMI()
|
|
||||||
battery_wmi = w.query('select * from Win32_Battery')[0]
|
|
||||||
battery_psutil = psutil.sensors_battery()
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
battery_psutil.percent, battery_wmi.EstimatedChargeRemaining,
|
|
||||||
delta=1)
|
|
||||||
|
|
||||||
@unittest.skipIf(not HAS_BATTERY, "no battery")
|
|
||||||
def test_power_plugged(self):
|
|
||||||
w = wmi.WMI()
|
|
||||||
battery_wmi = w.query('select * from Win32_Battery')[0]
|
|
||||||
battery_psutil = psutil.sensors_battery()
|
|
||||||
# Status codes:
|
|
||||||
# https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx
|
|
||||||
self.assertEqual(battery_psutil.power_plugged,
|
|
||||||
battery_wmi.BatteryStatus == 2)
|
|
||||||
|
|
||||||
def test_emulate_no_battery(self):
|
|
||||||
with mock.patch("psutil._pswindows.cext.sensors_battery",
|
|
||||||
return_value=(0, 128, 0, 0)) as m:
|
|
||||||
self.assertIsNone(psutil.sensors_battery())
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_emulate_power_connected(self):
|
|
||||||
with mock.patch("psutil._pswindows.cext.sensors_battery",
|
|
||||||
return_value=(1, 0, 0, 0)) as m:
|
|
||||||
self.assertEqual(psutil.sensors_battery().secsleft,
|
|
||||||
psutil.POWER_TIME_UNLIMITED)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_emulate_power_charging(self):
|
|
||||||
with mock.patch("psutil._pswindows.cext.sensors_battery",
|
|
||||||
return_value=(0, 8, 0, 0)) as m:
|
|
||||||
self.assertEqual(psutil.sensors_battery().secsleft,
|
|
||||||
psutil.POWER_TIME_UNLIMITED)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
def test_emulate_secs_left_unknown(self):
|
|
||||||
with mock.patch("psutil._pswindows.cext.sensors_battery",
|
|
||||||
return_value=(0, 0, 0, -1)) as m:
|
|
||||||
self.assertEqual(psutil.sensors_battery().secsleft,
|
|
||||||
psutil.POWER_TIME_UNKNOWN)
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# Process APIs
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestProcess(unittest.TestCase):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_issue_24(self):
|
|
||||||
p = psutil.Process(0)
|
|
||||||
self.assertRaises(psutil.AccessDenied, p.kill)
|
|
||||||
|
|
||||||
def test_special_pid(self):
|
|
||||||
p = psutil.Process(4)
|
|
||||||
self.assertEqual(p.name(), 'System')
|
|
||||||
# use __str__ to access all common Process properties to check
|
|
||||||
# that nothing strange happens
|
|
||||||
str(p)
|
|
||||||
p.username()
|
|
||||||
self.assertTrue(p.create_time() >= 0.0)
|
|
||||||
try:
|
|
||||||
rss, vms = p.memory_info()[:2]
|
|
||||||
except psutil.AccessDenied:
|
|
||||||
# expected on Windows Vista and Windows 7
|
|
||||||
if not platform.uname()[1] in ('vista', 'win-7', 'win7'):
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
self.assertTrue(rss > 0)
|
|
||||||
|
|
||||||
def test_send_signal(self):
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
self.assertRaises(ValueError, p.send_signal, signal.SIGINT)
|
|
||||||
|
|
||||||
def test_exe(self):
|
|
||||||
for p in psutil.process_iter():
|
|
||||||
try:
|
|
||||||
self.assertEqual(os.path.basename(p.exe()), p.name())
|
|
||||||
except psutil.Error:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_num_handles_increment(self):
|
|
||||||
p = psutil.Process(os.getpid())
|
|
||||||
before = p.num_handles()
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, os.getpid())
|
|
||||||
after = p.num_handles()
|
|
||||||
self.assertEqual(after, before + 1)
|
|
||||||
win32api.CloseHandle(handle)
|
|
||||||
self.assertEqual(p.num_handles(), before)
|
|
||||||
|
|
||||||
def test_handles_leak(self):
|
|
||||||
# Call all Process methods and make sure no handles are left
|
|
||||||
# open. This is here mainly to make sure functions using
|
|
||||||
# OpenProcess() always call CloseHandle().
|
|
||||||
def call(p, attr):
|
|
||||||
attr = getattr(p, name, None)
|
|
||||||
if attr is not None and callable(attr):
|
|
||||||
attr()
|
|
||||||
else:
|
|
||||||
attr
|
|
||||||
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
failures = []
|
|
||||||
for name in dir(psutil.Process):
|
|
||||||
if name.startswith('_') \
|
|
||||||
or name in ('terminate', 'kill', 'suspend', 'resume',
|
|
||||||
'nice', 'send_signal', 'wait', 'children',
|
|
||||||
'as_dict', 'memory_info_ex'):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
call(p, name)
|
|
||||||
num1 = p.num_handles()
|
|
||||||
call(p, name)
|
|
||||||
num2 = p.num_handles()
|
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if num2 > num1:
|
|
||||||
fail = \
|
|
||||||
"failure while processing Process.%s method " \
|
|
||||||
"(before=%s, after=%s)" % (name, num1, num2)
|
|
||||||
failures.append(fail)
|
|
||||||
if failures:
|
|
||||||
self.fail('\n' + '\n'.join(failures))
|
|
||||||
|
|
||||||
def test_name_always_available(self):
|
|
||||||
# On Windows name() is never supposed to raise AccessDenied,
|
|
||||||
# see https://github.com/giampaolo/psutil/issues/627
|
|
||||||
for p in psutil.process_iter():
|
|
||||||
try:
|
|
||||||
p.name()
|
|
||||||
except psutil.NoSuchProcess:
|
|
||||||
pass
|
|
||||||
|
|
||||||
@unittest.skipIf(not sys.version_info >= (2, 7),
|
|
||||||
"CTRL_* signals not supported")
|
|
||||||
def test_ctrl_signals(self):
|
|
||||||
p = psutil.Process(get_test_subprocess().pid)
|
|
||||||
p.send_signal(signal.CTRL_C_EVENT)
|
|
||||||
p.send_signal(signal.CTRL_BREAK_EVENT)
|
|
||||||
p.kill()
|
|
||||||
p.wait()
|
|
||||||
self.assertRaises(psutil.NoSuchProcess,
|
|
||||||
p.send_signal, signal.CTRL_C_EVENT)
|
|
||||||
self.assertRaises(psutil.NoSuchProcess,
|
|
||||||
p.send_signal, signal.CTRL_BREAK_EVENT)
|
|
||||||
|
|
||||||
def test_compare_name_exe(self):
|
|
||||||
for p in psutil.process_iter():
|
|
||||||
try:
|
|
||||||
a = os.path.basename(p.exe())
|
|
||||||
b = p.name()
|
|
||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.assertEqual(a, b)
|
|
||||||
|
|
||||||
def test_username(self):
|
|
||||||
self.assertEqual(psutil.Process().username(),
|
|
||||||
win32api.GetUserNameEx(win32con.NameSamCompatible))
|
|
||||||
|
|
||||||
def test_cmdline(self):
|
|
||||||
sys_value = re.sub(' +', ' ', win32api.GetCommandLine()).strip()
|
|
||||||
psutil_value = ' '.join(psutil.Process().cmdline())
|
|
||||||
self.assertEqual(sys_value, psutil_value)
|
|
||||||
|
|
||||||
# XXX - occasional failures
|
|
||||||
|
|
||||||
# def test_cpu_times(self):
|
|
||||||
# handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
# win32con.FALSE, os.getpid())
|
|
||||||
# self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
# sys_value = win32process.GetProcessTimes(handle)
|
|
||||||
# psutil_value = psutil.Process().cpu_times()
|
|
||||||
# self.assertAlmostEqual(
|
|
||||||
# psutil_value.user, sys_value['UserTime'] / 10000000.0,
|
|
||||||
# delta=0.2)
|
|
||||||
# self.assertAlmostEqual(
|
|
||||||
# psutil_value.user, sys_value['KernelTime'] / 10000000.0,
|
|
||||||
# delta=0.2)
|
|
||||||
|
|
||||||
def test_nice(self):
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, os.getpid())
|
|
||||||
self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
sys_value = win32process.GetPriorityClass(handle)
|
|
||||||
psutil_value = psutil.Process().nice()
|
|
||||||
self.assertEqual(psutil_value, sys_value)
|
|
||||||
|
|
||||||
def test_memory_info(self):
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, self.pid)
|
|
||||||
self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
sys_value = win32process.GetProcessMemoryInfo(handle)
|
|
||||||
psutil_value = psutil.Process(self.pid).memory_info()
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['PeakWorkingSetSize'], psutil_value.peak_wset)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['WorkingSetSize'], psutil_value.wset)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['QuotaPeakPagedPoolUsage'], psutil_value.peak_paged_pool)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['QuotaPagedPoolUsage'], psutil_value.paged_pool)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['QuotaPeakNonPagedPoolUsage'],
|
|
||||||
psutil_value.peak_nonpaged_pool)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['QuotaNonPagedPoolUsage'], psutil_value.nonpaged_pool)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['PagefileUsage'], psutil_value.pagefile)
|
|
||||||
self.assertEqual(
|
|
||||||
sys_value['PeakPagefileUsage'], psutil_value.peak_pagefile)
|
|
||||||
|
|
||||||
self.assertEqual(psutil_value.rss, psutil_value.wset)
|
|
||||||
self.assertEqual(psutil_value.vms, psutil_value.pagefile)
|
|
||||||
|
|
||||||
def test_wait(self):
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, self.pid)
|
|
||||||
self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
p.terminate()
|
|
||||||
psutil_value = p.wait()
|
|
||||||
sys_value = win32process.GetExitCodeProcess(handle)
|
|
||||||
self.assertEqual(psutil_value, sys_value)
|
|
||||||
|
|
||||||
def test_cpu_affinity(self):
|
|
||||||
def from_bitmask(x):
|
|
||||||
return [i for i in range(64) if (1 << i) & x]
|
|
||||||
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, self.pid)
|
|
||||||
self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
sys_value = from_bitmask(
|
|
||||||
win32process.GetProcessAffinityMask(handle)[0])
|
|
||||||
psutil_value = psutil.Process(self.pid).cpu_affinity()
|
|
||||||
self.assertEqual(psutil_value, sys_value)
|
|
||||||
|
|
||||||
def test_io_counters(self):
|
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
|
|
||||||
win32con.FALSE, os.getpid())
|
|
||||||
self.addCleanup(win32api.CloseHandle, handle)
|
|
||||||
sys_value = win32process.GetProcessIoCounters(handle)
|
|
||||||
psutil_value = psutil.Process().io_counters()
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.read_count, sys_value['ReadOperationCount'])
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.write_count, sys_value['WriteOperationCount'])
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.read_bytes, sys_value['ReadTransferCount'])
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.write_bytes, sys_value['WriteTransferCount'])
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.other_count, sys_value['OtherOperationCount'])
|
|
||||||
self.assertEqual(
|
|
||||||
psutil_value.other_bytes, sys_value['OtherTransferCount'])
|
|
||||||
|
|
||||||
def test_num_handles(self):
|
|
||||||
import ctypes
|
|
||||||
import ctypes.wintypes
|
|
||||||
PROCESS_QUERY_INFORMATION = 0x400
|
|
||||||
handle = ctypes.windll.kernel32.OpenProcess(
|
|
||||||
PROCESS_QUERY_INFORMATION, 0, os.getpid())
|
|
||||||
self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle)
|
|
||||||
hndcnt = ctypes.wintypes.DWORD()
|
|
||||||
ctypes.windll.kernel32.GetProcessHandleCount(
|
|
||||||
handle, ctypes.byref(hndcnt))
|
|
||||||
sys_value = hndcnt.value
|
|
||||||
psutil_value = psutil.Process().num_handles()
|
|
||||||
ctypes.windll.kernel32.CloseHandle(handle)
|
|
||||||
self.assertEqual(psutil_value, sys_value + 1)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestProcessWMI(unittest.TestCase):
|
|
||||||
"""Compare Process API results with WMI."""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_name(self):
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
self.assertEqual(p.name(), w.Caption)
|
|
||||||
|
|
||||||
def test_exe(self):
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
# Note: wmi reports the exe as a lower case string.
|
|
||||||
# Being Windows paths case-insensitive we ignore that.
|
|
||||||
self.assertEqual(p.exe().lower(), w.ExecutablePath.lower())
|
|
||||||
|
|
||||||
def test_cmdline(self):
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
self.assertEqual(' '.join(p.cmdline()),
|
|
||||||
w.CommandLine.replace('"', ''))
|
|
||||||
|
|
||||||
def test_username(self):
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
domain, _, username = w.GetOwner()
|
|
||||||
username = "%s\\%s" % (domain, username)
|
|
||||||
self.assertEqual(p.username(), username)
|
|
||||||
|
|
||||||
def test_memory_rss(self):
|
|
||||||
time.sleep(0.1)
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
rss = p.memory_info().rss
|
|
||||||
self.assertEqual(rss, int(w.WorkingSetSize))
|
|
||||||
|
|
||||||
def test_memory_vms(self):
|
|
||||||
time.sleep(0.1)
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
vms = p.memory_info().vms
|
|
||||||
# http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx
|
|
||||||
# ...claims that PageFileUsage is represented in Kilo
|
|
||||||
# bytes but funnily enough on certain platforms bytes are
|
|
||||||
# returned instead.
|
|
||||||
wmi_usage = int(w.PageFileUsage)
|
|
||||||
if (vms != wmi_usage) and (vms != wmi_usage * 1024):
|
|
||||||
self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms))
|
|
||||||
|
|
||||||
def test_create_time(self):
|
|
||||||
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
|
|
||||||
p = psutil.Process(self.pid)
|
|
||||||
wmic_create = str(w.CreationDate.split('.')[0])
|
|
||||||
psutil_create = time.strftime("%Y%m%d%H%M%S",
|
|
||||||
time.localtime(p.create_time()))
|
|
||||||
self.assertEqual(wmic_create, psutil_create)
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestDualProcessImplementation(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Certain APIs on Windows have 2 internal implementations, one
|
|
||||||
based on documented Windows APIs, another one based
|
|
||||||
NtQuerySystemInformation() which gets called as fallback in
|
|
||||||
case the first fails because of limited permission error.
|
|
||||||
Here we test that the two methods return the exact same value,
|
|
||||||
see:
|
|
||||||
https://github.com/giampaolo/psutil/issues/304
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
cls.pid = get_test_subprocess().pid
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
# ---
|
|
||||||
# same tests as above but mimicks the AccessDenied failure of
|
|
||||||
# the first (fast) method failing with AD.
|
|
||||||
|
|
||||||
def test_name(self):
|
|
||||||
name = psutil.Process(self.pid).name()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_exe",
|
|
||||||
side_effect=psutil.AccessDenied(os.getpid())) as fun:
|
|
||||||
self.assertEqual(psutil.Process(self.pid).name(), name)
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
def test_memory_info(self):
|
|
||||||
mem_1 = psutil.Process(self.pid).memory_info()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_memory_info",
|
|
||||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
|
||||||
mem_2 = psutil.Process(self.pid).memory_info()
|
|
||||||
self.assertEqual(len(mem_1), len(mem_2))
|
|
||||||
for i in range(len(mem_1)):
|
|
||||||
self.assertGreaterEqual(mem_1[i], 0)
|
|
||||||
self.assertGreaterEqual(mem_2[i], 0)
|
|
||||||
self.assertAlmostEqual(mem_1[i], mem_2[i], delta=512)
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
def test_create_time(self):
|
|
||||||
ctime = psutil.Process(self.pid).create_time()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_create_time",
|
|
||||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
|
||||||
self.assertEqual(psutil.Process(self.pid).create_time(), ctime)
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
def test_cpu_times(self):
|
|
||||||
cpu_times_1 = psutil.Process(self.pid).cpu_times()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_cpu_times",
|
|
||||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
|
||||||
cpu_times_2 = psutil.Process(self.pid).cpu_times()
|
|
||||||
assert fun.called
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
cpu_times_1.user, cpu_times_2.user, delta=0.01)
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
cpu_times_1.system, cpu_times_2.system, delta=0.01)
|
|
||||||
|
|
||||||
def test_io_counters(self):
|
|
||||||
io_counters_1 = psutil.Process(self.pid).io_counters()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_io_counters",
|
|
||||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
|
||||||
io_counters_2 = psutil.Process(self.pid).io_counters()
|
|
||||||
for i in range(len(io_counters_1)):
|
|
||||||
self.assertAlmostEqual(
|
|
||||||
io_counters_1[i], io_counters_2[i], delta=5)
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
def test_num_handles(self):
|
|
||||||
num_handles = psutil.Process(self.pid).num_handles()
|
|
||||||
with mock.patch("psutil._psplatform.cext.proc_num_handles",
|
|
||||||
side_effect=OSError(errno.EPERM, "msg")) as fun:
|
|
||||||
self.assertEqual(psutil.Process(self.pid).num_handles(),
|
|
||||||
num_handles)
|
|
||||||
assert fun.called
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class RemoteProcessTestCase(unittest.TestCase):
|
|
||||||
"""Certain functions require calling ReadProcessMemory.
|
|
||||||
This trivially works when called on the current process.
|
|
||||||
Check that this works on other processes, especially when they
|
|
||||||
have a different bitness.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def find_other_interpreter():
|
|
||||||
# find a python interpreter that is of the opposite bitness from us
|
|
||||||
code = "import sys; sys.stdout.write(str(sys.maxsize > 2**32))"
|
|
||||||
|
|
||||||
# XXX: a different and probably more stable approach might be to access
|
|
||||||
# the registry but accessing 64 bit paths from a 32 bit process
|
|
||||||
for filename in glob.glob(r"C:\Python*\python.exe"):
|
|
||||||
proc = subprocess.Popen(args=[filename, "-c", code],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT)
|
|
||||||
output, _ = proc.communicate()
|
|
||||||
if output == str(not IS_64_BIT):
|
|
||||||
return filename
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
other_python = cls.find_other_interpreter()
|
|
||||||
|
|
||||||
if other_python is None:
|
|
||||||
raise unittest.SkipTest(
|
|
||||||
"could not find interpreter with opposite bitness")
|
|
||||||
|
|
||||||
if IS_64_BIT:
|
|
||||||
cls.python64 = sys.executable
|
|
||||||
cls.python32 = other_python
|
|
||||||
else:
|
|
||||||
cls.python64 = other_python
|
|
||||||
cls.python32 = sys.executable
|
|
||||||
|
|
||||||
test_args = ["-c", "import sys; sys.stdin.read()"]
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
env = os.environ.copy()
|
|
||||||
env["THINK_OF_A_NUMBER"] = str(os.getpid())
|
|
||||||
self.proc32 = get_test_subprocess([self.python32] + self.test_args,
|
|
||||||
env=env,
|
|
||||||
stdin=subprocess.PIPE)
|
|
||||||
self.proc64 = get_test_subprocess([self.python64] + self.test_args,
|
|
||||||
env=env,
|
|
||||||
stdin=subprocess.PIPE)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.proc32.communicate()
|
|
||||||
self.proc64.communicate()
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
reap_children()
|
|
||||||
|
|
||||||
def test_cmdline_32(self):
|
|
||||||
p = psutil.Process(self.proc32.pid)
|
|
||||||
self.assertEqual(len(p.cmdline()), 3)
|
|
||||||
self.assertEqual(p.cmdline()[1:], self.test_args)
|
|
||||||
|
|
||||||
def test_cmdline_64(self):
|
|
||||||
p = psutil.Process(self.proc64.pid)
|
|
||||||
self.assertEqual(len(p.cmdline()), 3)
|
|
||||||
self.assertEqual(p.cmdline()[1:], self.test_args)
|
|
||||||
|
|
||||||
def test_cwd_32(self):
|
|
||||||
p = psutil.Process(self.proc32.pid)
|
|
||||||
self.assertEqual(p.cwd(), os.getcwd())
|
|
||||||
|
|
||||||
def test_cwd_64(self):
|
|
||||||
p = psutil.Process(self.proc64.pid)
|
|
||||||
self.assertEqual(p.cwd(), os.getcwd())
|
|
||||||
|
|
||||||
def test_environ_32(self):
|
|
||||||
p = psutil.Process(self.proc32.pid)
|
|
||||||
e = p.environ()
|
|
||||||
self.assertIn("THINK_OF_A_NUMBER", e)
|
|
||||||
self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid()))
|
|
||||||
|
|
||||||
def test_environ_64(self):
|
|
||||||
p = psutil.Process(self.proc64.pid)
|
|
||||||
e = p.environ()
|
|
||||||
self.assertIn("THINK_OF_A_NUMBER", e)
|
|
||||||
self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid()))
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# Windows services
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(not WINDOWS, "WINDOWS only")
|
|
||||||
class TestServices(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_win_service_iter(self):
|
|
||||||
valid_statuses = set([
|
|
||||||
"running",
|
|
||||||
"paused",
|
|
||||||
"start",
|
|
||||||
"pause",
|
|
||||||
"continue",
|
|
||||||
"stop",
|
|
||||||
"stopped",
|
|
||||||
])
|
|
||||||
valid_start_types = set([
|
|
||||||
"automatic",
|
|
||||||
"manual",
|
|
||||||
"disabled",
|
|
||||||
])
|
|
||||||
valid_statuses = set([
|
|
||||||
"running",
|
|
||||||
"paused",
|
|
||||||
"start_pending",
|
|
||||||
"pause_pending",
|
|
||||||
"continue_pending",
|
|
||||||
"stop_pending",
|
|
||||||
"stopped"
|
|
||||||
])
|
|
||||||
for serv in psutil.win_service_iter():
|
|
||||||
data = serv.as_dict()
|
|
||||||
self.assertIsInstance(data['name'], str)
|
|
||||||
self.assertNotEqual(data['name'].strip(), "")
|
|
||||||
self.assertIsInstance(data['display_name'], str)
|
|
||||||
self.assertIsInstance(data['username'], str)
|
|
||||||
self.assertIn(data['status'], valid_statuses)
|
|
||||||
if data['pid'] is not None:
|
|
||||||
psutil.Process(data['pid'])
|
|
||||||
self.assertIsInstance(data['binpath'], str)
|
|
||||||
self.assertIsInstance(data['username'], str)
|
|
||||||
self.assertIsInstance(data['start_type'], str)
|
|
||||||
self.assertIn(data['start_type'], valid_start_types)
|
|
||||||
self.assertIn(data['status'], valid_statuses)
|
|
||||||
self.assertIsInstance(data['description'], str)
|
|
||||||
pid = serv.pid()
|
|
||||||
if pid is not None:
|
|
||||||
p = psutil.Process(pid)
|
|
||||||
self.assertTrue(p.is_running())
|
|
||||||
# win_service_get
|
|
||||||
s = psutil.win_service_get(serv.name())
|
|
||||||
# test __eq__
|
|
||||||
self.assertEqual(serv, s)
|
|
||||||
|
|
||||||
def test_win_service_get(self):
|
|
||||||
name = next(psutil.win_service_iter()).name()
|
|
||||||
|
|
||||||
with self.assertRaises(psutil.NoSuchProcess) as cm:
|
|
||||||
psutil.win_service_get(name + '???')
|
|
||||||
self.assertEqual(cm.exception.name, name + '???')
|
|
||||||
|
|
||||||
# test NoSuchProcess
|
|
||||||
service = psutil.win_service_get(name)
|
|
||||||
exc = WindowsError(
|
|
||||||
psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST, "")
|
|
||||||
with mock.patch("psutil._psplatform.cext.winservice_query_status",
|
|
||||||
side_effect=exc):
|
|
||||||
self.assertRaises(psutil.NoSuchProcess, service.status)
|
|
||||||
with mock.patch("psutil._psplatform.cext.winservice_query_config",
|
|
||||||
side_effect=exc):
|
|
||||||
self.assertRaises(psutil.NoSuchProcess, service.username)
|
|
||||||
|
|
||||||
# test AccessDenied
|
|
||||||
exc = WindowsError(
|
|
||||||
psutil._psplatform.cext.ERROR_ACCESS_DENIED, "")
|
|
||||||
with mock.patch("psutil._psplatform.cext.winservice_query_status",
|
|
||||||
side_effect=exc):
|
|
||||||
self.assertRaises(psutil.AccessDenied, service.status)
|
|
||||||
with mock.patch("psutil._psplatform.cext.winservice_query_config",
|
|
||||||
side_effect=exc):
|
|
||||||
self.assertRaises(psutil.AccessDenied, service.username)
|
|
||||||
|
|
||||||
# test __str__ and __repr__
|
|
||||||
self.assertIn(service.name(), str(service))
|
|
||||||
self.assertIn(service.display_name(), str(service))
|
|
||||||
self.assertIn(service.name(), repr(service))
|
|
||||||
self.assertIn(service.display_name(), repr(service))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_test_module_by_name(__file__)
|
|
Loading…
Reference in New Issue