diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2015-11-06 17:14:18 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2015-11-06 17:14:18 +0100 |
commit | 4cffa4ce9cc900cb3a5f547deb06f46101d0a90b (patch) | |
tree | 9cc6d63c8a1ad3d0a1b6c0cb20e5e7f1b600d87a | |
parent | c83bca37349952a5d5c0d437d1649afb78c9a0ca (diff) | |
parent | afd4e5b0957b27c2121a8c37a893fdd9d40bae1c (diff) | |
download | psutil-4cffa4ce9cc900cb3a5f547deb06f46101d0a90b.tar.gz |
merge from master
-rwxr-xr-x | .git-pre-commit | 2 | ||||
-rw-r--r-- | .travis.yml | 4 | ||||
-rw-r--r-- | CREDITS | 8 | ||||
-rw-r--r-- | HISTORY.rst | 21 | ||||
-rw-r--r-- | README.rst | 1 | ||||
-rw-r--r-- | TODO | 28 | ||||
-rw-r--r-- | docs/index.rst | 28 | ||||
-rwxr-xr-x | examples/top.py | 5 | ||||
-rw-r--r-- | make.bat | 5 | ||||
-rw-r--r-- | psutil/__init__.py | 48 | ||||
-rw-r--r-- | psutil/_common.py | 22 | ||||
-rw-r--r-- | psutil/_compat.py | 6 | ||||
-rw-r--r-- | psutil/_psbsd.py | 4 | ||||
-rw-r--r-- | psutil/_pslinux.py | 171 | ||||
-rw-r--r-- | psutil/_psosx.py | 7 | ||||
-rw-r--r-- | psutil/_psposix.py | 7 | ||||
-rw-r--r-- | psutil/_pssunos.py | 12 | ||||
-rw-r--r-- | psutil/_psutil_bsd.c | 10 | ||||
-rw-r--r-- | psutil/_psutil_sunos.c | 8 | ||||
-rw-r--r-- | psutil/_pswindows.py | 24 | ||||
-rw-r--r-- | psutil/arch/windows/ntextapi.h | 7 | ||||
-rw-r--r-- | test/_freebsd.py | 11 | ||||
-rw-r--r-- | test/_linux.py | 75 | ||||
-rw-r--r-- | test/_osx.py | 11 | ||||
-rw-r--r-- | test/_posix.py | 20 | ||||
-rw-r--r-- | test/_sunos.py | 4 | ||||
-rw-r--r-- | test/_windows.py | 15 | ||||
-rw-r--r-- | test/test_memory_leaks.py | 21 | ||||
-rw-r--r-- | test/test_psutil.py | 52 |
29 files changed, 431 insertions, 206 deletions
diff --git a/.git-pre-commit b/.git-pre-commit index 3a6161f4..0b838d48 100755 --- a/.git-pre-commit +++ b/.git-pre-commit @@ -38,7 +38,7 @@ def main(): # flake8 failed = False for path in files: - ret = subprocess.call("flake8 %s" % path, shell=True) + ret = subprocess.call("python -m flake8 %s" % path, shell=True) if ret != 0: failed = True if failed: diff --git a/.travis.yml b/.travis.yml index d883c653..6ec09d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ matrix: include: - python: 2.6 - python: 2.7 - - python: 3.2 - python: 3.3 - python: 3.4 - language: generic @@ -15,9 +14,6 @@ matrix: env: PYVER=py27 - language: generic os: osx - env: PYVER=py32 - - language: generic - os: osx env: PYVER=py33 - language: generic os: osx @@ -329,3 +329,11 @@ I: 659 N: wiggin15 W: https://github.com/wiggin15 I: 517, 607, 610 + +N: dasumin +W: https://github.com/dasumin +I: 541 + +N: Mike Sarahan +W: https://github.com/msarahan +I: 688 diff --git a/HISTORY.rst b/HISTORY.rst index 52566b44..639c9d91 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,14 +1,33 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -3.2.2 - XXXX-XX-XX +3.2.3 - XXXX-XX-XX +================== + +**Enhancements** + +- #558: [Linux] exposed psutil.PROCFS_PATH constant to change the default + location of /proc filesystem. + +**Bug fixes** + +- #692: [UNIX] Process.name() is no longer cached as it may change. + + +3.2.2 - 2015-10-04 ================== **Bug fixes** - #517: [SunOS] net_io_counters failed to detect network interfaces correctly on Solaris 10 +- #541: [FreeBSD] disk_io_counters r/w times were expressed in seconds instead + of milliseconds. (patch by dasumin) - #610: [SunOS] fix build and tests on Solaris 10 +- #623: [Linux] process or system connections raises ValueError if IPv6 is not + supported by the system. - #678: [Linux] can't install psutil due to bug in setup.py. +- #688: [Windows] compilation fails with MSVC 2015, Python 3.5. (patch by + Mike Sarahan) 3.2.1 - 2015-09-03 @@ -350,6 +350,7 @@ http://groups.google.com/group/psutil/ Timeline ======== +- 2015-10-04: `psutil-3.2.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.2.2.tar.gz>`_ - 2015-09-03: `psutil-3.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.2.1.tar.gz>`_ - 2015-09-02: `psutil-3.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.2.0.tar.gz>`_ - 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_ @@ -6,11 +6,25 @@ A collection of ideas and notes about stuff to implement in future versions. https://github.com/giampaolo/psutil/issues +PLATFORMS +========= + + * #615 (PR): OpenBSD + + * #355 (patch): Android + + * #429 (patch): NetBSD + + * #605 (branch): AIX + + * #276: GNU/Hurd + + * DragonFlyBSD + + HIGHER PRIORITY =============== - * OpenBSD support. - * #371: CPU temperature (apparently OSX and Linux only; on Linux it requires lm-sensors lib). @@ -36,16 +50,6 @@ HIGHER PRIORITY LOWER PRIORITY ============== - * #355: Android support. - - * #276: GNU/Hurd support. - - * #429: NetBSD support. - - * DragonFlyBSD support? - - * AIX support? - * examples/taskmgr-gui.py (using tk). * system-wide number of open file descriptors: diff --git a/docs/index.rst b/docs/index.rst index e8998390..31ba1fc3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -455,9 +455,9 @@ Network .. note:: *netmask*, *broadcast* and *ptp* are not supported on Windows and are set to ``None``. - *New in 3.0.0* + .. versionadded:: 3.0.0 - *Changed in 3.2.0:* *ptp* field was added. + .. versionchanged:: 3.2.0 *ptp* field was added. .. function:: net_if_stats() @@ -484,7 +484,7 @@ Network {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500), 'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)} - *New in 3.0.0* + .. versionadded:: 3.0.0 Other system info @@ -615,7 +615,7 @@ Exceptions interested in retrieving zombies (e.g. when using :func:`process_iter()`) you can ignore this exception and just catch :class:`NoSuchProcess`. - *New in 3.0.0* + .. versionadded:: 3.0.0 .. class:: AccessDenied(pid=None, name=None, msg=None) @@ -679,7 +679,7 @@ Process class .. method:: name() - The process name. The return value is cached after first call. + The process name. .. method:: exe() @@ -1188,8 +1188,8 @@ Process class signals are supported and **SIGTERM** is treated as an alias for :meth:`kill()`. - *Changed in 3.2.0:* support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals - was added. + .. versionchanged:: 3.2.0 support for CTRL_C_EVENT and CTRL_BREAK_EVENT + signals was added. .. method:: suspend() @@ -1280,6 +1280,16 @@ Constants ========= .. _const-pstatus: +.. data:: PROCFS_PATH + + The path of the /proc filesystem on Linux (defaults to "/proc"). You may want + to re-set this in case /proc is mounted elsewhere. + + Availability: Linux + + .. versionadded:: 3.2.3 + +.. _const-pstatus: .. data:: STATUS_RUNNING STATUS_SLEEPING STATUS_DISK_SLEEP @@ -1394,7 +1404,7 @@ Constants Constant which identifies a MAC address associated with a network interface. To be used in conjunction with :func:`psutil.net_if_addrs()`. - *New in 3.0.0* + .. versionadded:: 3.0.0 .. _const-duplex: .. data:: NIC_DUPLEX_FULL @@ -1407,7 +1417,7 @@ Constants receive data at a time. To be used in conjunction with :func:`psutil.net_if_stats()`. - *New in 3.0.0* + .. versionadded:: 3.0.0 Development guide ================= diff --git a/examples/top.py b/examples/top.py index 7aebef1d..4807439c 100755 --- a/examples/top.py +++ b/examples/top.py @@ -34,11 +34,12 @@ PID USER NI VIRT RES CPU% MEM% TIME+ NAME ... """ -from datetime import datetime, timedelta +from datetime import datetime +from datetime import timedelta import atexit import os -import time import sys +import time try: import curses except ImportError: @@ -30,12 +30,14 @@ set PYTHON26=C:\Python26\python.exe set PYTHON27=C:\Python27\python.exe set PYTHON33=C:\Python33\python.exe set PYTHON34=C:\Python34\python.exe +set PYTHON35=C:\Python35\python.exe set PYTHON26-64=C:\Python26-64\python.exe set PYTHON27-64=C:\Python27-64\python.exe set PYTHON33-64=C:\Python33-64\python.exe set PYTHON34-64=C:\Python34-64\python.exe +set PYTHON35-64=C:\Python35-64\python.exe -set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64% +set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON35% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64% %PYTHON35-64% rem Needed to locate the .pypirc file and upload exes on PYPI. set HOME=%USERPROFILE% @@ -64,7 +66,6 @@ if "%1" == "clean" ( for /r %%R in (__pycache__) do if exist %%R (rmdir /S /Q %%R) for /r %%R in (*.pyc) do if exist %%R (del /s %%R) for /r %%R in (*.pyd) do if exist %%R (del /s %%R) - for /r %%R in (*.obj) do if exist %%R (del /s %%R) for /r %%R in (*.orig) do if exist %%R (del /s %%R) for /r %%R in (*.bak) do if exist %%R (del /s %%R) for /r %%R in (*.rej) do if exist %%R (del /s %%R) diff --git a/psutil/__init__.py b/psutil/__init__.py index 636bf5a8..048ac527 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -61,6 +61,10 @@ from ._common import (NIC_DUPLEX_FULL, # NOQA if sys.platform.startswith("linux"): from . import _pslinux as _psplatform + # This is public API and it will be retrieved from _pslinux.py + # via sys.modules. + PROCFS_PATH = "/proc" + from ._pslinux import (IOPRIO_CLASS_NONE, # NOQA IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, @@ -157,7 +161,7 @@ __all__ = [ ] __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "3.2.2" +__version__ = "3.2.3" version_info = tuple([int(num) for num in __version__.split('.')]) AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None @@ -519,25 +523,29 @@ class Process(object): def name(self): """The process name. The return value is cached after first call.""" - if self._name is None: - name = self._proc.name() - if _POSIX and len(name) >= 15: - # On UNIX the name gets truncated to the first 15 characters. - # If it matches the first part of the cmdline we return that - # one instead because it's usually more explicative. - # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". - try: - cmdline = self.cmdline() - except AccessDenied: - pass - else: - if cmdline: - extended_name = os.path.basename(cmdline[0]) - if extended_name.startswith(name): - name = extended_name - self._proc._name = name - self._name = name - return self._name + # Process name is only cached on Windows as on POSIX it may + # change, see: + # https://github.com/giampaolo/psutil/issues/692 + if _WINDOWS and self._name is not None: + return self._name + name = self._proc.name() + if _POSIX and len(name) >= 15: + # On UNIX the name gets truncated to the first 15 characters. + # If it matches the first part of the cmdline we return that + # one instead because it's usually more explicative. + # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". + try: + cmdline = self.cmdline() + except AccessDenied: + pass + else: + if cmdline: + extended_name = os.path.basename(cmdline[0]) + if extended_name.startswith(name): + name = extended_name + self._name = name + self._proc._name = name + return name def exe(self): """The process executable as an absolute path. diff --git a/psutil/_common.py b/psutil/_common.py index 9f5c06f2..29acfb8d 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -1,5 +1,3 @@ -# /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. @@ -7,6 +5,8 @@ """Common objects shared by all _ps* modules.""" from __future__ import division + +import contextlib import errno import functools import os @@ -14,7 +14,10 @@ import socket import stat import sys from collections import namedtuple -from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM +from socket import AF_INET +from socket import SOCK_DGRAM +from socket import SOCK_STREAM + try: import threading except ImportError: @@ -140,6 +143,19 @@ def isfile_strict(path): return stat.S_ISREG(st.st_mode) +def supports_ipv6(): + """Return True if IPv6 is supported on this platform.""" + if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"): + return False + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + with contextlib.closing(sock): + sock.bind(("::1", 0)) + return True + except socket.error: + return False + + def sockfam_to_enum(num): """Convert a numeric socket family value to an IntEnum member. If it's not a known member, return the numeric value itself. diff --git a/psutil/_compat.py b/psutil/_compat.py index 3d984998..f79af3ac 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -20,6 +20,9 @@ if PY3: def u(s): return s + + def b(s): + return s.encode("latin-1") else: long = long xrange = xrange @@ -28,6 +31,9 @@ else: def u(s): return unicode(s, "unicode_escape") + def b(s): + return s + # removed in 3.0, reintroduced in 3.2 try: diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 1c147033..a381ef39 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -15,8 +15,10 @@ from . import _common from . import _psposix from . import _psutil_bsd as cext from . import _psutil_posix as cext_posix -from ._common import conn_tmap, usage_percent, sockfam_to_enum +from ._common import conn_tmap +from ._common import sockfam_to_enum from ._common import socktype_to_enum +from ._common import usage_percent from ._compat import which diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 54a2f916..5731851b 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -15,15 +15,22 @@ import socket import struct import sys import warnings -from collections import namedtuple, defaultdict +from collections import defaultdict +from collections import namedtuple from . import _common from . import _psposix from . import _psutil_linux as cext from . import _psutil_posix as cext_posix -from ._common import isfile_strict, usage_percent -from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN -from ._compat import PY3, long +from ._common import isfile_strict +from ._common import NIC_DUPLEX_FULL +from ._common import NIC_DUPLEX_HALF +from ._common import NIC_DUPLEX_UNKNOWN +from ._common import supports_ipv6 +from ._common import usage_percent +from ._compat import b +from ._compat import long +from ._compat import PY3 if sys.version_info >= (3, 4): import enum @@ -32,6 +39,8 @@ else: __extra__all__ = [ + # + 'PROCFS_PATH', # io prio constants "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", "IOPRIO_CLASS_IDLE", @@ -124,15 +133,20 @@ def open_text(fname): return open(fname, "rt", **kw) +def get_procfs_path(): + return sys.modules['psutil'].PROCFS_PATH + + # --- named tuples -def _get_cputimes_fields(): +def set_scputimes_ntuple(procfs_path): """Return a namedtuple of variable fields depending on the CPU times available on this Linux kernel version which may be: (user, nice, system, idle, iowait, irq, softirq, [steal, [guest, [guest_nice]]]) """ - with open('/proc/stat', 'rb') as f: + global scputimes + with open('%s/stat' % procfs_path, 'rb') as f: values = f.readline().split()[1:] fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq'] vlen = len(values) @@ -145,10 +159,11 @@ def _get_cputimes_fields(): if vlen >= 10: # Linux >= 3.2.0 fields.append('guest_nice') - return fields + scputimes = namedtuple('scputimes', fields) + return scputimes -scputimes = namedtuple('scputimes', _get_cputimes_fields()) +scputimes = set_scputimes_ntuple('/proc') svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', @@ -170,7 +185,7 @@ pmmap_ext = namedtuple( def virtual_memory(): total, free, buffers, shared, _, _ = cext.linux_sysinfo() cached = active = inactive = None - with open('/proc/meminfo', 'rb') as f: + with open('%s/meminfo' % get_procfs_path(), 'rb') as f: for line in f: if line.startswith(b"Cached:"): cached = int(line.split()[1]) * 1024 @@ -201,7 +216,7 @@ def swap_memory(): used = total - free percent = usage_percent(used, total, _round=1) # get pgin/pgouts - with open("/proc/vmstat", "rb") as f: + with open("%s/vmstat" % get_procfs_path(), "rb") as f: sin = sout = None for line in f: # values are expressed in 4 kilo bytes, we want bytes instead @@ -230,7 +245,9 @@ def cpu_times(): [guest_nice]]]) Last 3 fields may not be available on all Linux kernel versions. """ - with open('/proc/stat', 'rb') as f: + procfs_path = get_procfs_path() + set_scputimes_ntuple(procfs_path) + with open('%s/stat' % procfs_path, 'rb') as f: values = f.readline().split() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] @@ -241,8 +258,10 @@ def per_cpu_times(): """Return a list of namedtuple representing the CPU times for every CPU available on the system. """ + procfs_path = get_procfs_path() + set_scputimes_ntuple(procfs_path) cpus = [] - with open('/proc/stat', 'rb') as f: + with open('%s/stat' % procfs_path, 'rb') as f: # get rid of the first line which refers to system wide CPU stats f.readline() for line in f: @@ -262,7 +281,7 @@ def cpu_count_logical(): except ValueError: # as a second fallback we try to parse /proc/cpuinfo num = 0 - with open('/proc/cpuinfo', 'rb') as f: + with open('%s/cpuinfo' % get_procfs_path(), 'rb') as f: for line in f: if line.lower().startswith(b'processor'): num += 1 @@ -272,7 +291,7 @@ def cpu_count_logical(): # try to parse /proc/stat as a last resort if num == 0: search = re.compile('cpu\d') - with open_text('/proc/stat') as f: + with open_text('%s/stat' % get_procfs_path()) as f: for line in f: line = line.split(' ')[0] if search.match(line): @@ -288,7 +307,7 @@ def cpu_count_physical(): """Return the number of physical cores in the system.""" mapping = {} current_info = {} - with open('/proc/cpuinfo', 'rb') as f: + with open('%s/cpuinfo' % get_procfs_path(), 'rb') as f: for line in f: line = line.strip().lower() if not line: @@ -332,20 +351,21 @@ def users(): def boot_time(): """Return the system boot time expressed in seconds since the epoch.""" global BOOT_TIME - with open('/proc/stat', 'rb') as f: + with open('%s/stat' % get_procfs_path(), 'rb') as f: for line in f: if line.startswith(b'btime'): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret - raise RuntimeError("line 'btime' not found in /proc/stat") + raise RuntimeError( + "line 'btime' not found in %s/stat" % get_procfs_path()) # --- processes def pids(): """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(b'/proc') if x.isdigit()] + return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] def pid_exists(pid): @@ -355,6 +375,10 @@ def pid_exists(pid): # --- network +class _Ipv6UnsupportedError(Exception): + pass + + class Connections: """A wrapper on top of /proc/net/* files, retrieving per-process and system-wide open connections (TCP, UDP, UNIX) similarly to @@ -386,12 +410,14 @@ class Connections: "inet4": (tcp4, udp4), "inet6": (tcp6, udp6), } + self._procfs_path = None def get_proc_inodes(self, pid): inodes = defaultdict(list) - for fd in os.listdir("/proc/%s/fd" % pid): + for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)): try: - inode = os.readlink("/proc/%s/fd/%s" % (pid, fd)) + inode = os.readlink("%s/%s/fd/%s" % ( + self._procfs_path, pid, fd)) except OSError as err: # ENOENT == file which is gone in the meantime; # os.stat('/proc/%s' % self.pid) will be done later @@ -464,15 +490,22 @@ class Connections: # return socket.inet_ntop(socket.AF_INET6, # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) ip = base64.b16decode(ip) - # see: https://github.com/giampaolo/psutil/issues/201 - if sys.byteorder == 'little': - ip = socket.inet_ntop( - socket.AF_INET6, - struct.pack('>4I', *struct.unpack('<4I', ip))) - else: - ip = socket.inet_ntop( - socket.AF_INET6, - struct.pack('<4I', *struct.unpack('<4I', ip))) + try: + # see: https://github.com/giampaolo/psutil/issues/201 + if sys.byteorder == 'little': + ip = socket.inet_ntop( + socket.AF_INET6, + struct.pack('>4I', *struct.unpack('<4I', ip))) + else: + ip = socket.inet_ntop( + socket.AF_INET6, + struct.pack('<4I', *struct.unpack('<4I', ip))) + except ValueError: + # see: https://github.com/giampaolo/psutil/issues/623 + if not supports_ipv6(): + raise _Ipv6UnsupportedError + else: + raise return (ip, port) def process_inet(self, file, family, type_, inodes, filter_pid=None): @@ -507,8 +540,11 @@ class Connections: status = TCP_STATUSES[status] else: status = _common.CONN_NONE - laddr = self.decode_address(laddr, family) - raddr = self.decode_address(raddr, family) + try: + laddr = self.decode_address(laddr, family) + raddr = self.decode_address(raddr, family) + except _Ipv6UnsupportedError: + continue yield (fd, family, type_, laddr, raddr, status, pid) def process_unix(self, file, family, inodes, filter_pid=None): @@ -548,6 +584,7 @@ class Connections: if kind not in self.tmap: raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in self.tmap]))) + self._procfs_path = get_procfs_path() if pid is not None: inodes = self.get_proc_inodes(pid) if not inodes: @@ -559,10 +596,12 @@ class Connections: for f, family, type_ in self.tmap[kind]: if family in (socket.AF_INET, socket.AF_INET6): ls = self.process_inet( - "/proc/net/%s" % f, family, type_, inodes, filter_pid=pid) + "%s/net/%s" % (self._procfs_path, f), + family, type_, inodes, filter_pid=pid) else: ls = self.process_unix( - "/proc/net/%s" % f, family, inodes, filter_pid=pid) + "%s/net/%s" % (self._procfs_path, f), + family, inodes, filter_pid=pid) for fd, family, type_, laddr, raddr, status, bound_pid in ls: if pid: conn = _common.pconn(fd, family, type_, laddr, raddr, @@ -586,7 +625,7 @@ def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ - with open_text("/proc/net/dev") as f: + with open_text("%s/net/dev" % get_procfs_path()) as f: lines = f.readlines() retdict = {} for line in lines[2:]: @@ -637,7 +676,7 @@ def disk_io_counters(): # determine partitions we want to look for partitions = [] - with open_text("/proc/partitions") as f: + with open_text("%s/partitions" % get_procfs_path()) as f: lines = f.readlines()[2:] for line in reversed(lines): _, _, _, name = line.split() @@ -654,7 +693,7 @@ def disk_io_counters(): partitions.append(name) # retdict = {} - with open_text("/proc/diskstats") as f: + with open_text("%s/diskstats" % get_procfs_path()) as f: lines = f.readlines() for line in lines: # http://www.mjmwired.net/kernel/Documentation/iostats.txt @@ -680,7 +719,7 @@ def disk_io_counters(): def disk_partitions(all=False): """Return mounted disk partitions as a list of namedtuples""" fstypes = set() - with open_text("/proc/filesystems") as f: + with open_text("%s/filesystems" % get_procfs_path()) as f: for line in f: line = line.strip() if not line.startswith("nodev"): @@ -750,29 +789,30 @@ def wrap_exceptions_w_zombie(fun): class Process(object): """Linux process implementation.""" - __slots__ = ["pid", "_name", "_ppid"] + __slots__ = ["pid", "_name", "_ppid", "_procfs_path"] def __init__(self, pid): self.pid = pid self._name = None self._ppid = None + self._procfs_path = get_procfs_path() @wrap_exceptions def name(self): - with open_text("/proc/%s/stat" % self.pid) as f: + with open_text("%s/%s/stat" % (self._procfs_path, self.pid)) as f: data = f.read() # XXX - gets changed later and probably needs refactoring return data[data.find('(') + 1:data.rfind(')')] def exe(self): try: - exe = os.readlink("/proc/%s/exe" % self.pid) + exe = os.readlink("%s/%s/exe" % (self._procfs_path, self.pid)) except OSError as err: if err.errno in (errno.ENOENT, errno.ESRCH): # no such file error; might be raised also if the # path actually exists for system processes with # low pids (about 0-20) - if os.path.lexists("/proc/%s" % self.pid): + if os.path.lexists("%s/%s" % (self._procfs_path, self.pid)): return "" else: if not pid_exists(self.pid): @@ -795,7 +835,7 @@ class Process(object): @wrap_exceptions def cmdline(self): - with open_text("/proc/%s/cmdline" % self.pid) as f: + with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f: data = f.read() if data.endswith('\x00'): data = data[:-1] @@ -804,7 +844,7 @@ class Process(object): @wrap_exceptions def terminal(self): tmap = _psposix._get_terminal_map() - with open("/proc/%s/stat" % self.pid, 'rb') as f: + with open("%s/%s/stat" % (self._procfs_path, self.pid), 'rb') as f: tty_nr = int(f.read().split(b' ')[6]) try: return tmap[tty_nr] @@ -814,7 +854,7 @@ class Process(object): if os.path.exists('/proc/%s/io' % os.getpid()): @wrap_exceptions def io_counters(self): - fname = "/proc/%s/io" % self.pid + fname = "%s/%s/io" % (self._procfs_path, self.pid) with open(fname, 'rb') as f: rcount = wcount = rbytes = wbytes = None for line in f: @@ -838,7 +878,7 @@ class Process(object): @wrap_exceptions def cpu_times(self): - with open("/proc/%s/stat" % self.pid, 'rb') as f: + with open("%s/%s/stat" % (self._procfs_path, self.pid), 'rb') as f: st = f.read().strip() # ignore the first two values ("pid (exe)") st = st[st.find(b')') + 2:] @@ -859,7 +899,7 @@ class Process(object): @wrap_exceptions def create_time(self): - with open("/proc/%s/stat" % self.pid, 'rb') as f: + with open("%s/%s/stat" % (self._procfs_path, self.pid), 'rb') as f: st = f.read().strip() # ignore the first two values ("pid (exe)") st = st[st.rfind(b')') + 2:] @@ -874,7 +914,7 @@ class Process(object): @wrap_exceptions def memory_info(self): - with open("/proc/%s/statm" % self.pid, 'rb') as f: + with open("%s/%s/statm" % (self._procfs_path, self.pid), 'rb') as f: vms, rss = f.readline().split()[:2] return _common.pmem(int(rss) * PAGESIZE, int(vms) * PAGESIZE) @@ -892,7 +932,7 @@ class Process(object): # | data | data + stack | drs | DATA | # | dirty | dirty pages (unused in Linux 2.6) | dt | | # ============================================================ - with open("/proc/%s/statm" % self.pid, "rb") as f: + with open("%s/%s/statm" % (self._procfs_path, self.pid), "rb") as f: vms, rss, shared, text, lib, data, dirty = \ [int(x) * PAGESIZE for x in f.readline().split()[:7]] return pextmem(rss, vms, shared, text, lib, data, dirty) @@ -905,7 +945,7 @@ class Process(object): Fields are explained in 'man proc'; here is an updated (Apr 2012) version: http://goo.gl/fmebo """ - with open_text("/proc/%s/smaps" % self.pid) as f: + with open_text("%s/%s/smaps" % (self._procfs_path, self.pid)) as f: first_line = f.readline() current_block = [first_line] @@ -969,13 +1009,13 @@ class Process(object): # readlink() might return paths containing null bytes causing # problems when used with other fs-related functions (os.*, # open(), ...) - path = os.readlink("/proc/%s/cwd" % self.pid) + path = os.readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) return path.replace('\x00', '') @wrap_exceptions def num_ctx_switches(self): vol = unvol = None - with open("/proc/%s/status" % self.pid, "rb") as f: + with open("%s/%s/status" % (self._procfs_path, self.pid), "rb") as f: for line in f: if line.startswith(b"voluntary_ctxt_switches"): vol = int(line.split()[1]) @@ -990,7 +1030,7 @@ class Process(object): @wrap_exceptions def num_threads(self): - with open("/proc/%s/status" % self.pid, "rb") as f: + with open("%s/%s/status" % (self._procfs_path, self.pid), "rb") as f: for line in f: if line.startswith(b"Threads:"): return int(line.split()[1]) @@ -998,12 +1038,13 @@ class Process(object): @wrap_exceptions def threads(self): - thread_ids = os.listdir("/proc/%s/task" % self.pid) + thread_ids = os.listdir("%s/%s/task" % (self._procfs_path, self.pid)) thread_ids.sort() retlist = [] hit_enoent = False for thread_id in thread_ids: - fname = "/proc/%s/task/%s/stat" % (self.pid, thread_id) + fname = "%s/%s/task/%s/stat" % ( + self._procfs_path, self.pid, thread_id) try: with open(fname, 'rb') as f: st = f.read().strip() @@ -1023,12 +1064,12 @@ class Process(object): retlist.append(ntuple) if hit_enoent: # raise NSP if the process disappeared on us - os.stat('/proc/%s' % self.pid) + os.stat('%s/%s' % (self._procfs_path, self.pid)) return retlist @wrap_exceptions def nice_get(self): - # with open_text('/proc/%s/stat' % self.pid) as f: + # with open_text('%s/%s/stat' % (self._procfs_path, self.pid)) as f: # data = f.read() # return int(data.split()[18]) @@ -1129,7 +1170,7 @@ class Process(object): @wrap_exceptions def status(self): - with open("/proc/%s/status" % self.pid, 'rb') as f: + with open("%s/%s/status" % (self._procfs_path, self.pid), 'rb') as f: for line in f: if line.startswith(b"State:"): letter = line.split()[1] @@ -1142,10 +1183,10 @@ class Process(object): @wrap_exceptions def open_files(self): retlist = [] - files = os.listdir("/proc/%s/fd" % self.pid) + files = os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)) hit_enoent = False for fd in files: - file = "/proc/%s/fd/%s" % (self.pid, fd) + file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd) try: file = os.readlink(file) except OSError as err: @@ -1168,23 +1209,23 @@ class Process(object): retlist.append(ntuple) if hit_enoent: # raise NSP if the process disappeared on us - os.stat('/proc/%s' % self.pid) + os.stat('%s/%s' % (self._procfs_path, self.pid)) return retlist @wrap_exceptions def connections(self, kind='inet'): ret = _connections.retrieve(kind, self.pid) # raise NSP if the process disappeared on us - os.stat('/proc/%s' % self.pid) + os.stat('%s/%s' % (self._procfs_path, self.pid)) return ret @wrap_exceptions def num_fds(self): - return len(os.listdir("/proc/%s/fd" % self.pid)) + return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))) @wrap_exceptions def ppid(self): - fpath = "/proc/%s/status" % self.pid + fpath = "%s/%s/status" % (self._procfs_path, self.pid) with open(fpath, 'rb') as f: for line in f: if line.startswith(b"PPid:"): @@ -1194,7 +1235,7 @@ class Process(object): @wrap_exceptions def uids(self): - fpath = "/proc/%s/status" % self.pid + fpath = "%s/%s/status" % (self._procfs_path, self.pid) with open(fpath, 'rb') as f: for line in f: if line.startswith(b'Uid:'): @@ -1204,7 +1245,7 @@ class Process(object): @wrap_exceptions def gids(self): - fpath = "/proc/%s/status" % self.pid + fpath = "%s/%s/status" % (self._procfs_path, self.pid) with open(fpath, 'rb') as f: for line in f: if line.startswith(b'Gid:'): diff --git a/psutil/_psosx.py b/psutil/_psosx.py index b44acb23..0e770b80 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -13,8 +13,11 @@ from . import _common from . import _psposix from . import _psutil_osx as cext from . import _psutil_posix as cext_posix -from ._common import conn_tmap, usage_percent, isfile_strict -from ._common import sockfam_to_enum, socktype_to_enum +from ._common import conn_tmap +from ._common import isfile_strict +from ._common import sockfam_to_enum +from ._common import socktype_to_enum +from ._common import usage_percent __extra__all__ = [] diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 046a75f0..02bbfcec 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -10,8 +10,11 @@ import os import sys import time -from ._common import sdiskusage, usage_percent, memoize -from ._compat import PY3, unicode +from ._common import memoize +from ._common import sdiskusage +from ._common import usage_percent +from ._compat import PY3 +from ._compat import unicode class TimeoutExpired(Exception): diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 10e439a4..c1574096 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -15,7 +15,9 @@ from . import _common from . import _psposix from . import _psutil_posix as cext_posix from . import _psutil_sunos as cext -from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum +from ._common import isfile_strict +from ._common import sockfam_to_enum +from ._common import socktype_to_enum from ._common import usage_percent from ._compat import PY3 @@ -211,7 +213,7 @@ def net_connections(kind, _pid=-1): raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in cmap]))) families, types = _common.conn_tmap[kind] - rawlist = cext.net_connections(_pid, families, types) + rawlist = cext.net_connections(_pid) ret = set() for item in rawlist: fd, fam, type_, laddr, raddr, status, pid = item @@ -284,7 +286,11 @@ class Process(object): @wrap_exceptions def exe(self): - # Will be guess later from cmdline but we want to explicitly + try: + return os.readlink("/proc/%s/path/a.out" % self.pid) + except OSError: + pass # continue and guess the exe name from the cmdline + # Will be guessed later from cmdline but we want to explicitly # invoke cmdline here in order to get an AccessDenied # exception if the user has not enough privileges. self.cmdline() diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 16bb351f..f9a366d3 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -63,7 +63,8 @@ // convert a timeval struct to a double #define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) - +// convert a bintime struct to milliseconds +#define BT2MSEC(bt) (bt.sec * 1000 + ( ( (uint64_t) 1000000000 * (uint32_t) (bt.frac >> 32) ) >> 32 ) / 1000000) /* * Utility function which fills a kinfo_proc struct based on process pid @@ -1555,10 +1556,9 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) { current.operations[DEVSTAT_WRITE], // no writes current.bytes[DEVSTAT_READ], // bytes read current.bytes[DEVSTAT_WRITE], // bytes written - (long long)devstat_compute_etime( - ¤t.duration[DEVSTAT_READ], NULL), // r time - (long long)devstat_compute_etime( - ¤t.duration[DEVSTAT_WRITE], NULL)); // w time + (long long) BT2MSEC(current.duration[DEVSTAT_READ]), // r time + (long long) BT2MSEC(current.duration[DEVSTAT_WRITE]) // w time + ); // finished transactions if (!py_disk_info) goto error; if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info)) diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c index 5910eb59..0a235d22 100644 --- a/psutil/_psutil_sunos.c +++ b/psutil/_psutil_sunos.c @@ -827,17 +827,11 @@ psutil_net_connections(PyObject *self, PyObject *args) { PyObject *py_tuple = NULL; PyObject *py_laddr = NULL; PyObject *py_raddr = NULL; - PyObject *py_af_filter = NULL; - PyObject *py_type_filter = NULL; if (py_retlist == NULL) return NULL; - if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter)) + if (! PyArg_ParseTuple(args, "l", &pid)) goto error; - if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) { - PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); - goto error; - } sd = open("/dev/arp", O_RDWR); if (sd == -1) { diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 5df17272..83f88765 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -12,15 +12,21 @@ from collections import namedtuple from . import _common from . import _psutil_windows as cext -from ._common import conn_tmap, usage_percent, isfile_strict -from ._common import sockfam_to_enum, socktype_to_enum -from ._compat import PY3, xrange, lru_cache, long -from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS, - BELOW_NORMAL_PRIORITY_CLASS, - HIGH_PRIORITY_CLASS, - IDLE_PRIORITY_CLASS, - NORMAL_PRIORITY_CLASS, - REALTIME_PRIORITY_CLASS) +from ._common import conn_tmap +from ._common import isfile_strict +from ._common import sockfam_to_enum +from ._common import socktype_to_enum +from ._common import usage_percent +from ._compat import long +from ._compat import lru_cache +from ._compat import PY3 +from ._compat import xrange +from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS +from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS +from ._psutil_windows import HIGH_PRIORITY_CLASS +from ._psutil_windows import IDLE_PRIORITY_CLASS +from ._psutil_windows import NORMAL_PRIORITY_CLASS +from ._psutil_windows import REALTIME_PRIORITY_CLASS if sys.version_info >= (3, 4): import enum diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index 7f86c57a..4f843e02 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -177,7 +177,6 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( DWORD ProcessInformationLength ); - typedef enum _PROCESSINFOCLASS2 { _ProcessBasicInformation, ProcessQuotaLimits, @@ -209,8 +208,12 @@ typedef enum _PROCESSINFOCLASS2 { /* added after XP+ */ _ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, +// MSVC 2015 starts forcing C++11 standard, which does not allow duplicate +// unscoped enumerations. It doesn't matter that this is C code, MSVC is a C++ compiler. +#if _MSC_VER < 1900 ProcessBreakOnTermination, - ProcessDebugObjectHandle, +#endif + ProcessDebugObjectHandle=ProcessLUIDDeviceMapsEnabled+2, ProcessDebugFlags, ProcessHandleTracing, ProcessIoPriority, diff --git a/test/_freebsd.py b/test/_freebsd.py index 8726c908..1aac6213 100644 --- a/test/_freebsd.py +++ b/test/_freebsd.py @@ -14,10 +14,15 @@ import sys import time import psutil - from psutil._compat import PY3 -from test_psutil import (MEMORY_TOLERANCE, FREEBSD, sh, get_test_subprocess, - which, retry_before_failing, reap_children, unittest) +from test_psutil import FREEBSD +from test_psutil import get_test_subprocess +from test_psutil import MEMORY_TOLERANCE +from test_psutil import reap_children +from test_psutil import retry_before_failing +from test_psutil import sh +from test_psutil import unittest +from test_psutil import which PAGESIZE = os.sysconf("SC_PAGE_SIZE") diff --git a/test/_linux.py b/test/_linux.py index c9e04a53..661a2c87 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -7,6 +7,7 @@ """Linux specific tests. These are implicitly run by test_psutil.py.""" from __future__ import division + import contextlib import errno import fcntl @@ -26,15 +27,27 @@ try: except ImportError: import mock # requires "pip install mock" -from test_psutil import POSIX, MEMORY_TOLERANCE, TRAVIS, LINUX -from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, - retry_before_failing, get_kernel_version, unittest, - which, call_until) - import psutil import psutil._pslinux -from psutil._compat import PY3, u - +from psutil._compat import PY3 +from psutil._compat import u +from test_psutil import call_until +from test_psutil import get_kernel_version +from test_psutil import get_test_subprocess +from test_psutil import LINUX +from test_psutil import MEMORY_TOLERANCE +from test_psutil import POSIX +from test_psutil import retry_before_failing +from test_psutil import sh +from test_psutil import skip_on_not_implemented +from test_psutil import TRAVIS +from test_psutil import unittest +from test_psutil import which + + +# procps-ng 3.3.10 changed the output format of free +# and removed the 'buffers/cache line' +OLD_PROCPS_NG_VERSION = 'buffers/cache' in sh('free') SIOCGIFADDR = 0x8915 SIOCGIFCONF = 0x8912 @@ -129,7 +142,9 @@ class LinuxSpecificTestCase(unittest.TestCase): @retry_before_failing() def test_vmem_used(self): lines = sh('free').split('\n')[1:] - used = int(lines[0].split()[2]) * 1024 + total = int(lines[0].split()[1]) + free = int(lines[0].split()[3]) + used = (total - free) * 1024 self.assertAlmostEqual(used, psutil.virtual_memory().used, delta=MEMORY_TOLERANCE) @@ -142,34 +157,32 @@ class LinuxSpecificTestCase(unittest.TestCase): @retry_before_failing() def test_vmem_buffers(self): - lines = sh('free').split('\n')[1:] - buffers = int(lines[0].split()[5]) * 1024 + buffers = int(sh('vmstat').split('\n')[2].split()[4]) * 1024 self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers, delta=MEMORY_TOLERANCE) @retry_before_failing() def test_vmem_cached(self): - lines = sh('free').split('\n')[1:] - cached = int(lines[0].split()[6]) * 1024 + cached = int(sh('vmstat').split('\n')[2].split()[5]) * 1024 self.assertAlmostEqual(cached, psutil.virtual_memory().cached, delta=MEMORY_TOLERANCE) def test_swapmem_total(self): lines = sh('free').split('\n')[1:] - total = int(lines[2].split()[1]) * 1024 + total = int(lines[2 if OLD_PROCPS_NG_VERSION else 1].split()[1]) * 1024 self.assertEqual(total, psutil.swap_memory().total) @retry_before_failing() def test_swapmem_used(self): lines = sh('free').split('\n')[1:] - used = int(lines[2].split()[2]) * 1024 + used = int(lines[2 if OLD_PROCPS_NG_VERSION else 1].split()[2]) * 1024 self.assertAlmostEqual(used, psutil.swap_memory().used, delta=MEMORY_TOLERANCE) @retry_before_failing() def test_swapmem_free(self): lines = sh('free').split('\n')[1:] - free = int(lines[2].split()[3]) * 1024 + free = int(lines[2 if OLD_PROCPS_NG_VERSION else 1].split()[1]) * 1024 self.assertAlmostEqual(free, psutil.swap_memory().free, delta=MEMORY_TOLERANCE) @@ -426,6 +439,38 @@ class LinuxSpecificTestCase(unittest.TestCase): assert ret self.assertEqual(ret[0].fstype, 'zfs') + @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError) + @mock.patch('psutil._pslinux.supports_ipv6', return_value=False) + def test_connections_ipv6_not_supported(self, supports_ipv6, inet_ntop): + # see: https://github.com/giampaolo/psutil/issues/623 + try: + s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + self.addCleanup(s.close) + s.bind(("::1", 0)) + except socket.error: + pass + psutil.net_connections(kind='inet6') + + def test_procfs_path(self): + tdir = tempfile.mkdtemp() + try: + psutil.PROCFS_PATH = tdir + self.assertRaises(IOError, psutil.virtual_memory) + self.assertRaises(IOError, psutil.swap_memory) + self.assertRaises(IOError, psutil.cpu_times) + self.assertRaises(IOError, psutil.cpu_times, percpu=True) + self.assertRaises(IOError, psutil.boot_time) + # self.assertRaises(IOError, psutil.pids) + self.assertRaises(IOError, psutil.net_connections) + self.assertRaises(IOError, psutil.net_io_counters) + self.assertRaises(IOError, psutil.net_if_stats) + self.assertRaises(IOError, psutil.disk_io_counters) + self.assertRaises(IOError, psutil.disk_partitions) + self.assertRaises(psutil.NoSuchProcess, psutil.Process) + finally: + psutil.PROCFS_PATH = "/proc" + os.rmdir(tdir) + # --- tests for specific kernel versions @unittest.skipUnless( diff --git a/test/_osx.py b/test/_osx.py index 2375a483..cfa16c1f 100644 --- a/test/_osx.py +++ b/test/_osx.py @@ -15,9 +15,14 @@ import time import psutil from psutil._compat import PY3 -from test_psutil import (MEMORY_TOLERANCE, OSX, sh, get_test_subprocess, - reap_children, retry_before_failing, unittest, - TRAVIS) +from test_psutil import get_test_subprocess +from test_psutil import MEMORY_TOLERANCE +from test_psutil import OSX +from test_psutil import reap_children +from test_psutil import retry_before_failing +from test_psutil import sh +from test_psutil import TRAVIS +from test_psutil import unittest PAGESIZE = os.sysconf("SC_PAGE_SIZE") diff --git a/test/_posix.py b/test/_posix.py index 63db0d7e..c5f26f78 100644 --- a/test/_posix.py +++ b/test/_posix.py @@ -13,12 +13,22 @@ import sys import time import psutil - from psutil._compat import PY3, callable -from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX, TRAVIS -from test_psutil import (get_test_subprocess, skip_on_access_denied, - retry_before_failing, reap_children, sh, unittest, - get_kernel_version, wait_for_pid) +from test_psutil import BSD +from test_psutil import get_kernel_version +from test_psutil import get_test_subprocess +from test_psutil import LINUX +from test_psutil import OSX +from test_psutil import POSIX +from test_psutil import PYTHON +from test_psutil import reap_children +from test_psutil import retry_before_failing +from test_psutil import sh +from test_psutil import skip_on_access_denied +from test_psutil import SUNOS +from test_psutil import TRAVIS +from test_psutil import unittest +from test_psutil import wait_for_pid def ps(cmd): diff --git a/test/_sunos.py b/test/_sunos.py index 7520afc2..58050b1e 100644 --- a/test/_sunos.py +++ b/test/_sunos.py @@ -9,8 +9,10 @@ import sys import os -from test_psutil import SUNOS, sh, unittest import psutil +from test_psutil import sh +from test_psutil import SUNOS +from test_psutil import unittest @unittest.skipUnless(SUNOS, "not a SunOS system") diff --git a/test/_windows.py b/test/_windows.py index 827cc449..d1e3935e 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -16,10 +16,8 @@ import sys import time import traceback -from test_psutil import APPVEYOR, WINDOWS -from test_psutil import get_test_subprocess, reap_children, unittest - import mock + try: import wmi except ImportError: @@ -30,8 +28,16 @@ try: except ImportError: win32api = win32con = None -from psutil._compat import PY3, callable, long import psutil +from psutil._compat import callable +from psutil._compat import long +from psutil._compat import PY3 +from test_psutil import APPVEYOR +from test_psutil import get_test_subprocess +from test_psutil import reap_children +from test_psutil import retry_before_failing +from test_psutil import unittest +from test_psutil import WINDOWS cext = psutil._psplatform.cext @@ -207,6 +213,7 @@ class WindowsSpecificTestCase(unittest.TestCase): self.assertEqual(wmi_pids, psutil_pids) @unittest.skipIf(wmi is None, "wmi module is not installed") + @retry_before_failing() def test_disks(self): ps_parts = psutil.disk_partitions(all=True) wmi_parts = wmi.WMI().Win32_LogicalDisk() diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py index 89167fdc..d7588b0c 100644 --- a/test/test_memory_leaks.py +++ b/test/test_memory_leaks.py @@ -20,12 +20,21 @@ import time import psutil import psutil._common - -from psutil._compat import xrange, callable -from test_psutil import (WINDOWS, POSIX, OSX, LINUX, SUNOS, BSD, TESTFN, - RLIMIT_SUPPORT, TRAVIS) -from test_psutil import (reap_children, supports_ipv6, safe_remove, - get_test_subprocess) +from psutil._compat import callable +from psutil._compat import xrange +from test_psutil import BSD +from test_psutil import get_test_subprocess +from test_psutil import LINUX +from test_psutil import OSX +from test_psutil import POSIX +from test_psutil import reap_children +from test_psutil import RLIMIT_SUPPORT +from test_psutil import safe_remove +from test_psutil import SUNOS +from test_psutil import supports_ipv6 +from test_psutil import TESTFN +from test_psutil import TRAVIS +from test_psutil import WINDOWS if sys.version_info < (2, 7): import unittest2 as unittest # https://pypi.python.org/pypi/unittest2 diff --git a/test/test_psutil.py b/test/test_psutil.py index 5c173990..bbaeecb9 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -42,7 +42,9 @@ import time import traceback import types import warnings -from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM +from socket import AF_INET +from socket import SOCK_DGRAM +from socket import SOCK_STREAM try: import ipaddress # python >= 3.3 except ImportError: @@ -53,7 +55,12 @@ except ImportError: import mock # requires "pip install mock" import psutil -from psutil._compat import PY3, callable, long, unicode, which +from psutil._common import supports_ipv6 +from psutil._compat import callable +from psutil._compat import long +from psutil._compat import PY3 +from psutil._compat import unicode +from psutil._compat import which if sys.version_info < (2, 7): import unittest2 as unittest # https://pypi.python.org/pypi/unittest2 @@ -484,23 +491,6 @@ def skip_on_not_implemented(only_if=None): return decorator -def supports_ipv6(): - """Return True if IPv6 is supported on this platform.""" - if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"): - return False - sock = None - try: - sock = socket.socket(AF_INET6, SOCK_STREAM) - sock.bind(("::1", 0)) - except (socket.error, socket.gaierror): - return False - else: - return True - finally: - if sock is not None: - sock.close() - - def create_temp_executable_file(suffix): tmpdir = None if TRAVIS and OSX: @@ -1721,6 +1711,7 @@ class TestProcess(unittest.TestCase): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) + @unittest.skipIf(SUNOS, "doesn't work on Solaris") def test_prog_w_funky_name(self): # Test that name(), exe() and cmdline() correctly handle programs # with funky chars such as spaces and ")", see: @@ -2917,6 +2908,29 @@ class TestMisc(unittest.TestCase): # docstring self.assertEqual(foo.__doc__, "foo docstring") + def test_supports_ipv6(self): + if supports_ipv6(): + with mock.patch('psutil._common.socket') as s: + s.has_ipv6 = False + assert not supports_ipv6() + with mock.patch('psutil._common.socket.socket', + side_effect=socket.error) as s: + assert not supports_ipv6() + assert s.called + with mock.patch('psutil._common.socket.socket', + side_effect=socket.gaierror) as s: + assert not supports_ipv6() + assert s.called + with mock.patch('psutil._common.socket.socket.bind', + side_effect=socket.gaierror) as s: + assert not supports_ipv6() + assert s.called + else: + if hasattr(socket, 'AF_INET6'): + with self.assertRaises(Exception): + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind(("::1", 0)) + def test_isfile_strict(self): from psutil._common import isfile_strict this_file = os.path.abspath(__file__) |