summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2016-10-19 02:26:47 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2016-10-19 02:26:47 +0200
commit9873308309d6be23c416af7553b8a3941f379b3b (patch)
tree31ae5098dfb3682064976c0aa9615fdfa538a62a
parentece3a4802f49eb89dd90e67b02e74f847c0c0ce1 (diff)
parentf923f4522f765152aa67e958b7f053cc7007a119 (diff)
downloadpsutil-9873308309d6be23c416af7553b8a3941f379b3b.tar.gz
merge from oneshot branch
-rwxr-xr-x.ci/travis/install.sh4
-rwxr-xr-x.ci/travis/run.sh14
-rwxr-xr-x.git-pre-commit79
-rw-r--r--CREDITS22
-rw-r--r--DEVGUIDE.rst15
-rw-r--r--HISTORY.rst2411
-rw-r--r--IDEAS16
-rw-r--r--INSTALL.rst48
-rw-r--r--MANIFEST.in3
-rw-r--r--Makefile145
-rw-r--r--README.rst59
-rw-r--r--appveyor.yml22
-rw-r--r--docs/_static/pmap-small.pngbin0 -> 169063 bytes
-rw-r--r--docs/_static/pmap.pngbin0 -> 269776 bytes
-rw-r--r--docs/_static/procinfo-small.pngbin0 -> 90122 bytes
-rw-r--r--docs/_static/procinfo.pngbin0 -> 159281 bytes
-rw-r--r--docs/_static/procsmem-small.pngbin0 -> 207059 bytes
-rw-r--r--docs/_static/procsmem.pngbin0 -> 326209 bytes
-rw-r--r--docs/_static/top-small.pngbin0 -> 137860 bytes
-rw-r--r--docs/_static/top.pngbin0 -> 215631 bytes
-rw-r--r--docs/index.rst269
-rw-r--r--docs/make.bat274
-rw-r--r--make.bat221
-rw-r--r--psutil/__init__.py88
-rw-r--r--psutil/_psbsd.py27
-rw-r--r--psutil/_pslinux.py258
-rw-r--r--psutil/_psosx.py106
-rw-r--r--psutil/_pssunos.py7
-rw-r--r--psutil/_psutil_bsd.c53
-rw-r--r--psutil/_psutil_common.c89
-rw-r--r--psutil/_psutil_common.h5
-rw-r--r--psutil/_psutil_linux.c38
-rw-r--r--psutil/_psutil_osx.c531
-rw-r--r--psutil/_psutil_posix.c113
-rw-r--r--psutil/_psutil_posix.h10
-rw-r--r--psutil/_psutil_sunos.c3
-rw-r--r--psutil/_psutil_windows.c198
-rw-r--r--psutil/_pswindows.py10
-rw-r--r--psutil/arch/bsd/freebsd.c79
-rw-r--r--psutil/arch/bsd/freebsd.h2
-rw-r--r--psutil/arch/bsd/freebsd_socks.c6
-rw-r--r--psutil/arch/bsd/netbsd.c87
-rw-r--r--psutil/arch/bsd/netbsd.h4
-rw-r--r--psutil/arch/bsd/openbsd.c47
-rw-r--r--psutil/arch/bsd/openbsd.h2
-rw-r--r--psutil/arch/osx/process_info.c90
-rw-r--r--psutil/arch/osx/process_info.h6
-rw-r--r--psutil/arch/windows/process_handles.c6
-rw-r--r--psutil/arch/windows/process_info.c5
-rw-r--r--psutil/tests/__init__.py385
-rwxr-xr-x[-rw-r--r--]psutil/tests/runner.py2
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_bsd.py141
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_linux.py450
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_memory_leaks.py47
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_misc.py214
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_osx.py87
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_posix.py13
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_process.py476
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_sunos.py2
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_system.py99
-rwxr-xr-x[-rw-r--r--]psutil/tests/test_windows.py31
-rwxr-xr-xscripts/disk_usage.py1
-rwxr-xr-xscripts/free.py1
-rwxr-xr-xscripts/ifconfig.py83
-rwxr-xr-xscripts/internal/bench_oneshot.py76
-rw-r--r--scripts/internal/bench_oneshot_2.py56
-rwxr-xr-xscripts/internal/download_exes.py (renamed from .ci/appveyor/download_exes.py)20
-rwxr-xr-xscripts/internal/print_announce.py116
-rwxr-xr-xscripts/internal/winmake.py322
-rwxr-xr-xscripts/iotop.py2
-rwxr-xr-xscripts/killall.py4
-rwxr-xr-xscripts/meminfo.py1
-rwxr-xr-xscripts/netstat.py1
-rwxr-xr-xscripts/nettop.py1
-rwxr-xr-xscripts/pidof.py1
-rwxr-xr-xscripts/pmap.py1
-rwxr-xr-xscripts/procinfo.py359
-rwxr-xr-xscripts/procsmem.py37
-rwxr-xr-xscripts/ps.py1
-rwxr-xr-xscripts/top.py15
-rwxr-xr-xscripts/who.py1
-rwxr-xr-x[-rw-r--r--]setup.py88
82 files changed, 5551 insertions, 3055 deletions
diff --git a/.ci/travis/install.sh b/.ci/travis/install.sh
index 5735b7a1..677dc465 100755
--- a/.ci/travis/install.sh
+++ b/.ci/travis/install.sh
@@ -42,7 +42,7 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
fi
if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]] || [[ $PYVER == 'py26' ]]; then
- pip install -U ipaddress unittest2 mock==1.0.1
+ pip install -U ipaddress unittest2 argparse mock==1.0.1
elif [[ $TRAVIS_PYTHON_VERSION == '2.7' ]] || [[ $PYVER == 'py27' ]]; then
pip install -U ipaddress mock
elif [[ $TRAVIS_PYTHON_VERSION == '3.2' ]] || [[ $PYVER == 'py32' ]]; then
@@ -51,4 +51,4 @@ elif [[ $TRAVIS_PYTHON_VERSION == '3.3' ]] || [[ $PYVER == 'py33' ]]; then
pip install -U ipaddress
fi
-pip install coverage coveralls flake8 pep8 setuptools
+pip install -U coverage coveralls flake8 pep8 setuptools
diff --git a/.ci/travis/run.sh b/.ci/travis/run.sh
index 4269f30c..e70b58b8 100755
--- a/.ci/travis/run.sh
+++ b/.ci/travis/run.sh
@@ -3,6 +3,9 @@
set -e
set -x
+PYVER=`python -c 'import sys; print(".".join(map(str, sys.version_info[:2])))'`
+
+# setup OSX
if [[ "$(uname -s)" == 'Darwin' ]]; then
if which pyenv > /dev/null; then
eval "$(pyenv init -)"
@@ -10,15 +13,22 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then
pyenv activate psutil
fi
+# install psutil
python setup.py build
python setup.py develop
+# run tests (with coverage)
if [[ "$(uname -s)" != 'Darwin' ]]; then
coverage run psutil/tests/runner.py --include="psutil/*" --omit="test/*,*setup*"
else
python psutil/tests/runner.py
fi
+# run mem leaks test
python psutil/tests/test_memory_leaks.py
-flake8
-pep8
+
+# run linters
+if [ "$PYVER" != "2.6" ]; then
+ flake8
+ pep8
+fi
diff --git a/.git-pre-commit b/.git-pre-commit
index 99387729..e15884d1 100755
--- a/.git-pre-commit
+++ b/.git-pre-commit
@@ -1,20 +1,60 @@
#!/usr/bin/env python
-# This gets executed on 'git commit' and rejects the commit in case the
-# submitted code does not pass validation.
-# Install it with "make install-git-hooks"
+# 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.
+"""
+This gets executed on 'git commit' and rejects the commit in case the
+submitted code does not pass validation.
+Install it with "make install-git-hooks".
+"""
+
+from __future__ import print_function
import os
import subprocess
import sys
+def term_supports_colors():
+ try:
+ import curses
+ assert sys.stderr.isatty()
+ curses.setupterm()
+ assert curses.tigetnum("colors") > 0
+ except Exception:
+ return False
+ else:
+ return True
+
+
+def hilite(s, ok=True, bold=False):
+ """Return an highlighted version of 'string'."""
+ attr = []
+ if ok is None: # no color
+ pass
+ elif ok: # green
+ attr.append('32')
+ else: # red
+ attr.append('31')
+ if bold:
+ attr.append('1')
+ return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
+
+
+def exit(msg):
+ if term_supports_colors():
+ msg = hilite(msg, ok=False)
+ print(msg, file=sys.stderr)
+ sys.exit(1)
+
+
def main():
out = subprocess.check_output("git diff --cached --name-only", shell=True)
- files = [x for x in out.split(b'\n') if x.endswith(b'.py') and
- os.path.exists(x)]
+ py_files = [x for x in out.split(b'\n') if x.endswith(b'.py') and
+ os.path.exists(x)]
- for path in files:
+ for path in py_files:
with open(path) as f:
data = f.read()
@@ -23,8 +63,8 @@ def main():
for lineno, line in enumerate(data.split('\n'), 1):
line = line.rstrip()
if "pdb.set_trace" in line:
- print("%s: %s" % (lineno, line))
- sys.exit(
+ print("%s:%s %s" % (path, lineno, line))
+ return exit(
"commit aborted: you forgot a pdb in your python code")
# bare except clause
@@ -32,16 +72,23 @@ def main():
for lineno, line in enumerate(data.split('\n'), 1):
line = line.rstrip()
if "except:" in line and not line.endswith("# NOQA"):
- print("%s: %s" % (lineno, line))
- sys.exit("commit aborted: bare except clause")
+ print("%s:%s %s" % (path, lineno, line))
+ return exit("commit aborted: bare except clause")
# flake8
- failed = False
- for path in files:
- ret = subprocess.call("python -m flake8 %s" % path, shell=True)
+ if py_files:
+ try:
+ import flake8 # NOQA
+ except ImportError:
+ return exit("commit aborted: flake8 is not installed; "
+ "run 'make setup-dev-env'")
+
+ # XXX: we should scape spaces and possibly other amenities here
+ ret = subprocess.call(
+ "%s -m flake8 %s" % (sys.executable, " ".join(py_files)),
+ shell=True)
if ret != 0:
- failed = True
- if failed:
- sys.exit("commit aborted: python code is not flake8-compliant")
+ return exit("commit aborted: python code is not flake8 compliant")
+
main()
diff --git a/CREDITS b/CREDITS
index 9d22fd83..4dcb888b 100644
--- a/CREDITS
+++ b/CREDITS
@@ -394,3 +394,25 @@ I: 816
N: Jeremy Humble
W: https://github.com/jhumble
I: 863
+
+N: Ilya Georgievsky
+W: https://github.com/xBeAsTx
+I: 870
+
+N: Yago Jesus
+W: https://github.com/YJesus
+I: 798
+
+N: Andre Caron
+C: Montreal, QC, Canada
+E: andre.l.caron@gmail.com
+W: https://github.com/AndreLouisCaron
+I: 880
+
+N: ewedlund
+W: https://github.com/ewedlund
+I: 874
+
+N: Arcadiy Ivanov
+W: https://github.com/arcivanov
+I: 919
diff --git a/DEVGUIDE.rst b/DEVGUIDE.rst
index ebd919ab..95bea79a 100644
--- a/DEVGUIDE.rst
+++ b/DEVGUIDE.rst
@@ -141,16 +141,11 @@ Documentation
Releasing a new version
=======================
-These are note for myself (Giampaolo):
-
-- make sure all tests pass and all builds are green.
-- upload source tarball on PYPI with ``make upload-src``.
-- upload exe and wheel files for windows on PYPI with ``make upload-all``.
- - ...or by using atrifacts hosted on AppVeyor with ``make win-download-exes``
- and ``make win-upload-exes``,
-- upload updated doc on http://pythonhosted.org/psutil with ``make upload-doc``.
-- GIT tag the new release with ``make git-tag-release``.
-- post on psutil and python-announce mailing lists, twitter, g+, blog.
+These are notes for myself (Giampaolo):
+
+- ``make release``
+- post announce (``make print-announce``) on psutil and python-announce mailing
+ lists, twitter, g+, blog.
=============
FreeBSD notes
diff --git a/HISTORY.rst b/HISTORY.rst
index 66076c8e..428b9074 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,217 +1,315 @@
-Bug tracker at https://github.com/giampaolo/psutil/issues
+*Bug tracker at https://github.com/giampaolo/psutil/issues*
-4.3.1 - XXXX-XX-XX
-==================
+4.4.1
+=====
-**Bug fixes**
+*2016-10-25*
-- #854: Process.as_dict() raises ValueError if passed an erroneous attrs name.
-- #857: [SunOS] Process cpu_times(), cpu_percent(), threads() amd memory_maps()
+Bug fixes
+---------
+
+- 927_: ``Popen.__del__`` may cause maximum recursion depth error.
+
+
+4.4.0
+=====
+
+*2016-10-23*
+
+Enhancements
+------------
+
+- 874_: [Windows] net_if_addrs() returns also the netmask.
+- 887_: [Linux] virtual_memory()'s 'available' and 'used' values are more
+ precise and match "free" cmdline utility. "available" also takes into
+ account LCX containers preventing "available" to overflow "total".
+- 891_: procinfo.py script has been updated and provides a lot more info.
+
+Bug fixes
+---------
+
+- 514_: [OSX] possibly fix Process.memory_maps() segfault (critical!).
+- 783_: [OSX] Process.status() may erroneously return "running" for zombie
+ processes.
+- 798_: [Windows] Process.open_files() returns and empty list on Windows 10.
+- 825_: [Linux] cpu_affinity; fix possible double close and use of unopened
+ socket.
+- 880_: [Windows] Handle race condition inside psutil_net_connections.
+- 885_: ValueError is raised if a negative integer is passed to cpu_percent()
+ functions.
+- 892_: [Linux] Process.cpu_affinity([-1]) raise SystemError with no error
+ set; now ValueError is raised.
+- 906_: [BSD] disk_partitions(all=False) returned an empty list. Now the
+ argument is ignored and all partitions are always returned.
+- 907_: [FreeBSD] Process.exe() may fail with OSError(ENOENT).
+- 908_: [OSX, BSD] different process methods could errounesuly mask the real
+ error for high-privileged PIDs and raise NoSuchProcess and AccessDenied
+ instead of OSError and RuntimeError.
+- 909_: [OSX] Process open_files() and connections() methods may raise
+ OSError with no exception set if process is gone.
+- 916_: [OSX] fix many compilation warnings.
+
+
+4.3.1
+=====
+
+*2016-09-01*
+
+Enhancements
+------------
+
+- 881_: "make install" now works also when using a virtual env.
+
+Bug fixes
+---------
+
+- 854_: Process.as_dict() raises ValueError if passed an erroneous attrs name.
+- 857_: [SunOS] Process cpu_times(), cpu_percent(), threads() amd memory_maps()
may raise RuntimeError if attempting to query a 64bit process with a 32bit
python. "Null" values are returned as a fallback.
-- #858: Process.as_dict() should not return memory_info_ex() because it's
+- 858_: Process.as_dict() should not return memory_info_ex() because it's
deprecated.
-- #863: [Windows] memory_map truncates addresses above 32 bits
-- #866: [Windows] win_service_iter() and services in general are not able to
+- 863_: [Windows] memory_map truncates addresses above 32 bits
+- 866_: [Windows] win_service_iter() and services in general are not able to
handle unicode service names / descriptions.
+- 869_: [Windows] Process.wait() may raise TimeoutExpired with wrong timeout
+ unit (ms instead of sec).
+- 870_: [Windows] Handle leak inside psutil_get_process_data.
-4.3.0 - 2016-06-18
-==================
+4.3.0
+=====
-**Enhancements**
+*2016-06-18*
-- #819: [Linux] different speedup improvements:
+Enhancements
+------------
+
+- 819_: [Linux] different speedup improvements:
Process.ppid() is 20% faster
Process.status() is 28% faster
Process.name() is 25% faster
Process.num_threads is 20% faster on Python 3
-**Bug fixes**
+Bug fixes
+---------
-- #810: [Windows] Windows wheels are incompatible with pip 7.1.2.
-- #812: [NetBSD] fix compilation on NetBSD-5.x.
-- #823: [NetBSD] virtual_memory() raises TypeError on Python 3.
-- #829: [UNIX] psutil.disk_usage() percent field takes root reserved space
+- 810_: [Windows] Windows wheels are incompatible with pip 7.1.2.
+- 812_: [NetBSD] fix compilation on NetBSD-5.x.
+- 823_: [NetBSD] virtual_memory() raises TypeError on Python 3.
+- 829_: [UNIX] psutil.disk_usage() percent field takes root reserved space
into account.
-- #816: [Windows] fixed net_io_counter() values wrapping after 4.3GB in
+- 816_: [Windows] fixed net_io_counter() values wrapping after 4.3GB in
Windows Vista (NT 6.0) and above using 64bit values from newer win APIs.
-4.2.0 - 2016-05-14
-==================
+4.2.0
+=====
+
+*2016-05-14*
-**Enhancements**
+Enhancements
+------------
-- #795: [Windows] new APIs to deal with Windows services: win_service_iter()
+- 795_: [Windows] new APIs to deal with Windows services: win_service_iter()
and win_service_get().
-- #800: [Linux] psutil.virtual_memory() returns a new "shared" memory field.
-- #819: [Linux] speedup /proc parsing:
+- 800_: [Linux] psutil.virtual_memory() returns a new "shared" memory field.
+- 819_: [Linux] speedup /proc parsing:
- Process.ppid() is 20% faster
- Process.status() is 28% faster
- Process.name() is 25% faster
- Process.num_threads is 20% faster on Python 3
-**Bug fixes**
+Bug fixes
+---------
-- #797: [Linux] net_if_stats() may raise OSError for certain NIC cards.
-- #813: Process.as_dict() should ignore extraneous attribute names which gets
+- 797_: [Linux] net_if_stats() may raise OSError for certain NIC cards.
+- 813_: Process.as_dict() should ignore extraneous attribute names which gets
attached to the Process instance.
-4.1.0 - 2016-03-12
-==================
+4.1.0
+=====
-**Enhancements**
+*2016-03-12*
-- #777: [Linux] Process.open_files() on Linux return 3 new fields: position,
+Enhancements
+------------
+
+- 777_: [Linux] Process.open_files() on Linux return 3 new fields: position,
mode and flags.
-- #779: Process.cpu_times() returns two new fields, 'children_user' and
+- 779_: Process.cpu_times() returns two new fields, 'children_user' and
'children_system' (always set to 0 on OSX and Windows).
-- #789: [Windows] psutil.cpu_times() return two new fields: "interrupt" and
+- 789_: [Windows] psutil.cpu_times() return two new fields: "interrupt" and
"dpc". Same for psutil.cpu_times_percent().
-- #792: new psutil.cpu_stats() function returning number of CPU ctx switches
+- 792_: new psutil.cpu_stats() function returning number of CPU ctx switches
interrupts, soft interrupts and syscalls.
-**Bug fixes**
+Bug fixes
+---------
-- #774: [FreeBSD] net_io_counters() dropout is no longer set to 0 if the kernel
+- 774_: [FreeBSD] net_io_counters() dropout is no longer set to 0 if the kernel
provides it.
-- #776: [Linux] Process.cpu_affinity() may erroneously raise NoSuchProcess.
+- 776_: [Linux] Process.cpu_affinity() may erroneously raise NoSuchProcess.
(patch by wxwright)
-- #780: [OSX] psutil does not compile with some gcc versions.
-- #786: net_if_addrs() may report incomplete MAC addresses.
-- #788: [NetBSD] virtual_memory()'s buffers and shared values were set to 0.
-- #790: [OSX] psutil won't compile on OSX 10.4.
+- 780_: [OSX] psutil does not compile with some gcc versions.
+- 786_: net_if_addrs() may report incomplete MAC addresses.
+- 788_: [NetBSD] virtual_memory()'s buffers and shared values were set to 0.
+- 790_: [OSX] psutil won't compile on OSX 10.4.
+
+4.0.0
+=====
-4.0.0 - 2016-02-17
-==================
+*2016-02-17*
-**Enhancements**
+Enhancements
+------------
-- #523: [Linux, FreeBSD] disk_io_counters() return a new "busy_time" field.
-- #660: [Windows] make.bat is smarter in finding alternative VS install
+- 523_: [Linux, FreeBSD] disk_io_counters() return a new "busy_time" field.
+- 660_: [Windows] make.bat is smarter in finding alternative VS install
locations. (patch by mpderbec)
-- #732: Process.environ(). (patch by Frank Benkstein)
-- #753: [Linux, OSX, Windows] Process USS and PSS (Linux) "real" memory stats.
+- 732_: Process.environ(). (patch by Frank Benkstein)
+- 753_: [Linux, OSX, Windows] Process USS and PSS (Linux) "real" memory stats.
(patch by Eric Rahm)
-- #755: Process.memory_percent() "memtype" parameter.
-- #758: tests now live in psutil namespace.
-- #760: expose OS constants (psutil.LINUX, psutil.OSX, etc.)
-- #756: [Linux] disk_io_counters() return 2 new fields: read_merged_count and
+- 755_: Process.memory_percent() "memtype" parameter.
+- 758_: tests now live in psutil namespace.
+- 760_: expose OS constants (psutil.LINUX, psutil.OSX, etc.)
+- 756_: [Linux] disk_io_counters() return 2 new fields: read_merged_count and
write_merged_count.
-- #762: new scripts/procsmem.py script.
+- 762_: new scripts/procsmem.py script.
-**Bug fixes**
+Bug fixes
+---------
-- #685: [Linux] virtual_memory() provides wrong results on systems with a lot
+- 685_: [Linux] virtual_memory() provides wrong results on systems with a lot
of physical memory.
-- #704: [Solaris] psutil does not compile on Solaris sparc.
-- #734: on Python 3 invalid UTF-8 data is not correctly handled for process
+- 704_: [Solaris] psutil does not compile on Solaris sparc.
+- 734_: on Python 3 invalid UTF-8 data is not correctly handled for process
name(), cwd(), exe(), cmdline() and open_files() methods resulting in
UnicodeDecodeError exceptions. 'surrogateescape' error handler is now
used as a workaround for replacing the corrupted data.
-- #737: [Windows] when the bitness of psutil and the target process was
+- 737_: [Windows] when the bitness of psutil and the target process was
different cmdline() and cwd() could return a wrong result or incorrectly
report an AccessDenied error.
-- #741: [OpenBSD] psutil does not compile on mips64.
-- #751: [Linux] fixed call to Py_DECREF on possible Null object.
-- #754: [Linux] cmdline() can be wrong in case of zombie process.
-- #759: [Linux] Process.memory_maps() may return paths ending with " (deleted)"
-- #761: [Windows] psutil.boot_time() wraps to 0 after 49 days.
-- #764: [NetBSD] fix compilation on NetBSD-6.x.
-- #766: [Linux] net_connections() can't handle malformed /proc/net/unix file.
-- #767: [Linux] disk_io_counters() may raise ValueError on 2.6 kernels and it's
+- 741_: [OpenBSD] psutil does not compile on mips64.
+- 751_: [Linux] fixed call to Py_DECREF on possible Null object.
+- 754_: [Linux] cmdline() can be wrong in case of zombie process.
+- 759_: [Linux] Process.memory_maps() may return paths ending with " (deleted)"
+- 761_: [Windows] psutil.boot_time() wraps to 0 after 49 days.
+- 764_: [NetBSD] fix compilation on NetBSD-6.x.
+- 766_: [Linux] net_connections() can't handle malformed /proc/net/unix file.
+- 767_: [Linux] disk_io_counters() may raise ValueError on 2.6 kernels and it's
broken on 2.4 kernels.
-- #770: [NetBSD] disk_io_counters() metrics didn't update.
+- 770_: [NetBSD] disk_io_counters() metrics didn't update.
-3.4.2 - 2016-01-20
-==================
+3.4.2
+=====
-**Enhancements**
+*2016-01-20*
-- #728: [Solaris] exposed psutil.PROCFS_PATH constant to change the default
+Enhancements
+------------
+
+- 728_: [Solaris] exposed psutil.PROCFS_PATH constant to change the default
location of /proc filesystem.
-**Bug fixes**
+Bug fixes
+---------
+
+- 724_: [FreeBSD] psutil.virtual_memory().total is incorrect.
+- 730_: [FreeBSD] psutil.virtual_memory() crashes.
-- #724: [FreeBSD] psutil.virtual_memory().total is incorrect.
-- #730: [FreeBSD] psutil.virtual_memory() crashes.
+3.4.1
+=====
-3.4.1 - 2016-01-15
-==================
+*2016-01-15*
-**Enhancements**
+Enhancements
+------------
-- #557: [NetBSD] added NetBSD support. (contributed by Ryo Onodera and
+- 557_: [NetBSD] added NetBSD support. (contributed by Ryo Onodera and
Thomas Klausner)
-- #708: [Linux] psutil.net_connections() and Process.connections() on Python 2
+- 708_: [Linux] psutil.net_connections() and Process.connections() on Python 2
can be up to 3x faster in case of many connections.
Also psutil.Process.memory_maps() is slightly faster.
-- #718: process_iter() is now thread safe.
+- 718_: process_iter() is now thread safe.
-**Bug fixes**
+Bug fixes
+---------
-- #714: [OpenBSD] virtual_memory().cached value was always set to 0.
-- #715: don't crash at import time if cpu_times() fail for some reason.
-- #717: [Linux] Process.open_files fails if deleted files still visible.
-- #722: [Linux] swap_memory() no longer crashes if sin/sout can't be determined
+- 714_: [OpenBSD] virtual_memory().cached value was always set to 0.
+- 715_: don't crash at import time if cpu_times() fail for some reason.
+- 717_: [Linux] Process.open_files fails if deleted files still visible.
+- 722_: [Linux] swap_memory() no longer crashes if sin/sout can't be determined
due to missing /proc/vmstat.
-- #724: [FreeBSD] virtual_memory().total is slightly incorrect.
+- 724_: [FreeBSD] virtual_memory().total is slightly incorrect.
+
+3.3.0
+=====
-3.3.0 - 2015-11-25
-==================
+*2015-11-25*
-**Enhancements**
+Enhancements
+------------
-- #558: [Linux] exposed psutil.PROCFS_PATH constant to change the default
+- 558_: [Linux] exposed psutil.PROCFS_PATH constant to change the default
location of /proc filesystem.
-- #615: [OpenBSD] added OpenBSD support. (contributed by Landry Breuil)
+- 615_: [OpenBSD] added OpenBSD support. (contributed by Landry Breuil)
-**Bug fixes**
+Bug fixes
+---------
-- #692: [UNIX] Process.name() is no longer cached as it may change.
+- 692_: [UNIX] Process.name() is no longer cached as it may change.
-3.2.2 - 2015-10-04
-==================
+3.2.2
+=====
-**Bug fixes**
+*2015-10-04*
-- #517: [SunOS] net_io_counters failed to detect network interfaces
+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
+- 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
+- 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
+- 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
-==================
+3.2.1
+=====
+
+*2015-09-03*
-**Bug fixes**
+Bug fixes
+---------
-- #677: [Linux] can't install psutil due to bug in setup.py.
+- 677_: [Linux] can't install psutil due to bug in setup.py.
-3.2.0 - 2015-09-02
-==================
+3.2.0
+=====
-**Enhancements**
+*2015-09-02*
-- #644: [Windows] added support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals
+Enhancements
+------------
+
+- 644_: [Windows] added support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals
to use with Process.send_signal().
-- #648: CI test integration for OSX. (patch by Jeff Tang)
-- #663: [UNIX] net_if_addrs() now returns point-to-point (VPNs) addresses.
-- #655: [Windows] different issues regarding unicode handling were fixed. On
+- 648_: CI test integration for OSX. (patch by Jeff Tang)
+- 663_: [UNIX] net_if_addrs() now returns point-to-point (VPNs) addresses.
+- 655_: [Windows] different issues regarding unicode handling were fixed. On
Python 2 all APIs returning a string will now return an encoded version of it
by using sys.getfilesystemencoding() codec. The APIs involved are:
- psutil.net_if_addrs()
@@ -222,262 +320,302 @@ Bug tracker at https://github.com/giampaolo/psutil/issues
- psutil.Process.username()
- psutil.users()
-**Bug fixes**
+Bug fixes
+---------
-- #513: [Linux] fixed integer overflow for RLIM_INFINITY.
-- #641: [Windows] fixed many compilation warnings. (patch by Jeff Tang)
-- #652: [Windows] net_if_addrs() UnicodeDecodeError in case of non-ASCII NIC
+- 513_: [Linux] fixed integer overflow for RLIM_INFINITY.
+- 641_: [Windows] fixed many compilation warnings. (patch by Jeff Tang)
+- 652_: [Windows] net_if_addrs() UnicodeDecodeError in case of non-ASCII NIC
names.
-- #655: [Windows] net_if_stats() UnicodeDecodeError in case of non-ASCII NIC
+- 655_: [Windows] net_if_stats() UnicodeDecodeError in case of non-ASCII NIC
names.
-- #659: [Linux] compilation error on Suse 10. (patch by maozguttman)
-- #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef)
-- #670: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names.
+- 659_: [Linux] compilation error on Suse 10. (patch by maozguttman)
+- 664_: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef)
+- 670_: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names.
(patch by sk6249)
-- #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by
+- 672_: [Windows] compilation fails if using Windows SDK v8.0. (patch by
Steven Winfield)
-- #675: [Linux] net_connections(); UnicodeDecodeError may occur when listing
+- 675_: [Linux] net_connections(); UnicodeDecodeError may occur when listing
UNIX sockets.
-3.1.1 - 2015-07-15
-==================
+3.1.1
+=====
+
+*2015-07-15*
-**Bug fixes**
+Bug fixes
+---------
-- #603: [Linux] ionice_set value range is incorrect. (patch by spacewander)
-- #645: [Linux] psutil.cpu_times_percent() may produce negative results.
-- #656: 'from psutil import *' does not work.
+- 603_: [Linux] ionice_set value range is incorrect. (patch by spacewander)
+- 645_: [Linux] psutil.cpu_times_percent() may produce negative results.
+- 656_: 'from psutil import *' does not work.
-3.1.0 - 2015-07-15
-==================
+3.1.0
+=====
-**Enhancements**
+*2015-07-15*
-- #534: [Linux] disk_partitions() added support for ZFS filesystems.
-- #646: continuous tests integration for Windows with
+Enhancements
+------------
+
+- 534_: [Linux] disk_partitions() added support for ZFS filesystems.
+- 646_: continuous tests integration for Windows with
https://ci.appveyor.com/project/giampaolo/psutil.
-- #647: new dev guide:
+- 647_: new dev guide:
https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst
-- #651: continuous code quality test integration with
+- 651_: continuous code quality test integration with
https://scrutinizer-ci.com/g/giampaolo/psutil/
-**Bug fixes**
+Bug fixes
+---------
-- #340: [Windows] Process.open_files() no longer hangs. Instead it uses a
+- 340_: [Windows] Process.open_files() no longer hangs. Instead it uses a
thred which times out and skips the file handle in case it's taking too long
to be retrieved. (patch by Jeff Tang, PR #597)
-- #627: [Windows] Process.name() no longer raises AccessDenied for pids owned
+- 627_: [Windows] Process.name() no longer raises AccessDenied for pids owned
by another user.
-- #636: [Windows] Process.memory_info() raise AccessDenied.
-- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it
+- 636_: [Windows] Process.memory_info() raise AccessDenied.
+- 637_: [UNIX] raise exception if trying to send signal to Process PID 0 as it
will affect os.getpid()'s process group instead of PID 0.
-- #639: [Linux] Process.cmdline() can be truncated.
-- #640: [Linux] *connections functions may swallow errors and return an
+- 639_: [Linux] Process.cmdline() can be truncated.
+- 640_: [Linux] *connections functions may swallow errors and return an
incomplete list of connnections.
-- #642: repr() of exceptions is incorrect.
-- #653: [Windows] Add inet_ntop function for Windows XP to support IPv6.
-- #641: [Windows] Replace deprecated string functions with safe equivalents.
+- 642_: repr() of exceptions is incorrect.
+- 653_: [Windows] Add inet_ntop function for Windows XP to support IPv6.
+- 641_: [Windows] Replace deprecated string functions with safe equivalents.
+
+3.0.1
+=====
-3.0.1 - 2015-06-18
-==================
+*2015-06-18*
-**Bug fixes**
+Bug fixes
+---------
-- #632: [Linux] better error message if cannot parse process UNIX connections.
-- #634: [Linux] Proces.cmdline() does not include empty string arguments.
-- #635: [UNIX] crash on module import if 'enum' package is installed on python
+- 632_: [Linux] better error message if cannot parse process UNIX connections.
+- 634_: [Linux] Proces.cmdline() does not include empty string arguments.
+- 635_: [UNIX] crash on module import if 'enum' package is installed on python
< 3.4.
-3.0.0 - 2015-06-13
-==================
+3.0.0
+=====
+
+*2015-06-13*
-**Enhancements**
+Enhancements
+------------
-- #250: new psutil.net_if_stats() returning NIC statistics (isup, duplex,
+- 250_: new psutil.net_if_stats() returning NIC statistics (isup, duplex,
speed, MTU).
-- #376: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig.
-- #469: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants
+- 376_: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig.
+- 469_: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants
returned by psutil.Process' ionice() and nice() methods are enums instead of
plain integers.
-- #581: add .gitignore. (patch by Gabi Davar)
-- #582: connection constants returned by psutil.net_connections() and
+- 581_: add .gitignore. (patch by Gabi Davar)
+- 582_: connection constants returned by psutil.net_connections() and
psutil.Process.connections() were turned from int to enums on Python > 3.4.
-- #587: Move native extension into the package.
-- #589: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
+- 587_: Move native extension into the package.
+- 589_: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
not only lists.
-- #594: all deprecated APIs were removed.
-- #599: [Windows] process name() can now be determined for all processes even
+- 594_: all deprecated APIs were removed.
+- 599_: [Windows] process name() can now be determined for all processes even
when running as a limited user.
-- #602: pre-commit GIT hook.
-- #629: enhanced support for py.test and nose test discovery and tests run.
-- #616: [Windows] Add inet_ntop function for Windows XP.
+- 602_: pre-commit GIT hook.
+- 629_: enhanced support for py.test and nose test discovery and tests run.
+- 616_: [Windows] Add inet_ntop function for Windows XP.
-**Bug fixes**
+Bug fixes
+---------
-- #428: [all UNIXes except Linux] correct handling of zombie processes;
+- 428_: [all UNIXes except Linux] correct handling of zombie processes;
introduced new ZombieProcess exception class.
-- #512: [BSD] fix segfault in net_connections().
-- #555: [Linux] psutil.users() correctly handles ":0" as an alias for
+- 512_: [BSD] fix segfault in net_connections().
+- 555_: [Linux] psutil.users() correctly handles ":0" as an alias for
"localhost"
-- #579: [Windows] Fixed open_files() for PID>64K.
-- #579: [Windows] fixed many compiler warnings.
-- #585: [FreeBSD] net_connections() may raise KeyError.
-- #586: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU
+- 579_: [Windows] Fixed open_files() for PID>64K.
+- 579_: [Windows] fixed many compiler warnings.
+- 585_: [FreeBSD] net_connections() may raise KeyError.
+- 586_: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU
number is provided.
-- #593: [FreeBSD] Process().memory_maps() segfaults.
-- #606: Process.parent() may swallow NoSuchProcess exceptions.
-- #611: [SunOS] net_io_counters has send and received swapped
-- #614: [Linux]: cpu_count(logical=False) return the number of physical CPUs
+- 593_: [FreeBSD] Process().memory_maps() segfaults.
+- 606_: Process.parent() may swallow NoSuchProcess exceptions.
+- 611_: [SunOS] net_io_counters has send and received swapped
+- 614_: [Linux]: cpu_count(logical=False) return the number of physical CPUs
instead of physical cores.
-- #618: [SunOS] swap tests fail on Solaris when run as normal user
-- #628: [Linux] Process.name() truncates process name in case it contains
+- 618_: [SunOS] swap tests fail on Solaris when run as normal user
+- 628_: [Linux] Process.name() truncates process name in case it contains
spaces or parentheses.
-2.2.1 - 2015-02-02
-==================
+2.2.1
+=====
-**Bug fixes**
+*2015-02-02*
-- #496: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references"
+Bug fixes
+---------
+
+- 496_: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references"
(patch by Bruno Binet)
-2.2.0 - 2015-01-06
-==================
+2.2.0
+=====
+
+*2015-01-06*
-**Enhancements**
+Enhancements
+------------
-- #521: drop support for Python 2.4 and 2.5.
-- #553: new examples/pstree.py script.
-- #564: C extension version mismatch in case the user messed up with psutil
+- 521_: drop support for Python 2.4 and 2.5.
+- 553_: new examples/pstree.py script.
+- 564_: C extension version mismatch in case the user messed up with psutil
installation or with sys.path is now detected at import time.
-- #568: New examples/pidof.py script.
-- #569: [FreeBSD] add support for process CPU affinity.
+- 568_: New examples/pidof.py script.
+- 569_: [FreeBSD] add support for process CPU affinity.
-**Bug fixes**
+Bug fixes
+---------
-- #496: [Solaris] can't import psutil.
-- #547: [UNIX] Process.username() may raise KeyError if UID can't be resolved.
-- #551: [Windows] get rid of the unicode hack for net_io_counters() NIC names.
-- #556: [Linux] lots of file handles were left open.
-- #561: [Linux] net_connections() might skip some legitimate UNIX sockets.
+- 496_: [Solaris] can't import psutil.
+- 547_: [UNIX] Process.username() may raise KeyError if UID can't be resolved.
+- 551_: [Windows] get rid of the unicode hack for net_io_counters() NIC names.
+- 556_: [Linux] lots of file handles were left open.
+- 561_: [Linux] net_connections() might skip some legitimate UNIX sockets.
(patch by spacewander)
-- #565: [Windows] use proper encoding for psutil.Process.username() and
+- 565_: [Windows] use proper encoding for psutil.Process.username() and
psutil.users(). (patch by Sylvain Mouquet)
-- #567: [Linux] in the alternative implementation of CPU affinity PyList_Append
+- 567_: [Linux] in the alternative implementation of CPU affinity PyList_Append
and Py_BuildValue return values are not checked.
-- #569: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False).
-- #571: [Linux] Process.open_files() might swallow AccessDenied exceptions and
+- 569_: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False).
+- 571_: [Linux] Process.open_files() might swallow AccessDenied exceptions and
return an incomplete list of open files.
-2.1.3 - 2014-09-26
-==================
+2.1.3
+=====
+
+*2014-09-26*
+
+- 536_: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error.
-- #536: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error.
+2.1.2
+=====
-2.1.2 - 2014-09-21
-==================
+*2014-09-21*
-**Enhancements**
+Enhancements
+------------
-- #407: project moved from Google Code to Github; code moved from Mercurial
+- 407_: project moved from Google Code to Github; code moved from Mercurial
to Git.
-- #492: use tox to run tests on multiple python versions. (patch by msabramo)
-- #505: [Windows] distribution as wheel packages.
-- #511: new examples/ps.py sample code.
+- 492_: use tox to run tests on multiple python versions. (patch by msabramo)
+- 505_: [Windows] distribution as wheel packages.
+- 511_: new examples/ps.py sample code.
-**Bug fixes**
+Bug fixes
+---------
-- #340: [Windows] Process.get_open_files() no longer hangs. (patch by
+- 340_: [Windows] Process.get_open_files() no longer hangs. (patch by
Jeff Tang)
-- #501: [Windows] disk_io_counters() may return negative values.
-- #503: [Linux] in rare conditions Process exe(), open_files() and
+- 501_: [Windows] disk_io_counters() may return negative values.
+- 503_: [Linux] in rare conditions Process exe(), open_files() and
connections() methods can raise OSError(ESRCH) instead of NoSuchProcess.
-- #504: [Linux] can't build RPM packages via setup.py
-- #506: [Linux] python 2.4 support was broken.
-- #522: [Linux] Process.cpu_affinity() might return EINVAL. (patch by David
+- 504_: [Linux] can't build RPM packages via setup.py
+- 506_: [Linux] python 2.4 support was broken.
+- 522_: [Linux] Process.cpu_affinity() might return EINVAL. (patch by David
Daeschler)
-- #529: [Windows] Process.exe() may raise unhandled WindowsError exception
+- 529_: [Windows] Process.exe() may raise unhandled WindowsError exception
for PIDs 0 and 4. (patch by Jeff Tang)
-- #530: [Linux] psutil.disk_io_counters() may crash on old Linux distros
+- 530_: [Linux] psutil.disk_io_counters() may crash on old Linux distros
(< 2.6.5) (patch by Yaolong Huang)
-- #533: [Linux] Process.memory_maps() may raise TypeError on old Linux distros.
+- 533_: [Linux] Process.memory_maps() may raise TypeError on old Linux distros.
-2.1.1 - 2014-04-30
-==================
+2.1.1
+=====
-**Bug fixes**
+*2014-04-30*
-- #446: [Windows] fix encoding error when using net_io_counters() on Python 3.
+Bug fixes
+---------
+
+- 446_: [Windows] fix encoding error when using net_io_counters() on Python 3.
(patch by Szigeti Gabor Niif)
-- #460: [Windows] net_io_counters() wraps after 4G.
-- #491: [Linux] psutil.net_connections() exceptions. (patch by Alexander Grothe)
+- 460_: [Windows] net_io_counters() wraps after 4G.
+- 491_: [Linux] psutil.net_connections() exceptions. (patch by Alexander Grothe)
+
+2.1.0
+=====
-2.1.0 - 2014-04-08
-==================
+*2014-04-08*
-**Enhancements**
+Enhancements
+------------
-- #387: system-wide open connections a-la netstat.
+- 387_: system-wide open connections a-la netstat.
-**Bug fixes**
+Bug fixes
+---------
-- #421: [Solaris] psutil does not compile on SunOS 5.10 (patch by Naveed
+- 421_: [Solaris] psutil does not compile on SunOS 5.10 (patch by Naveed
Roudsari)
-- #489: [Linux] psutil.disk_partitions() return an empty list.
+- 489_: [Linux] psutil.disk_partitions() return an empty list.
-2.0.0 - 2014-03-10
-==================
+2.0.0
+=====
-**Enhancements**
+*2014-03-10*
-- #424: [Windows] installer for Python 3.X 64 bit.
-- #427: number of logical and physical CPUs (psutil.cpu_count()).
-- #447: psutil.wait_procs() timeout parameter is now optional.
-- #452: make Process instances hashable and usable with set()s.
-- #453: tests on Python < 2.7 require unittest2 module.
-- #459: add a make file for running tests and other repetitive tasks (also
+Enhancements
+------------
+
+- 424_: [Windows] installer for Python 3.X 64 bit.
+- 427_: number of logical and physical CPUs (psutil.cpu_count()).
+- 447_: psutil.wait_procs() timeout parameter is now optional.
+- 452_: make Process instances hashable and usable with set()s.
+- 453_: tests on Python < 2.7 require unittest2 module.
+- 459_: add a make file for running tests and other repetitive tasks (also
on Windows).
-- #463: make timeout parameter of cpu_percent* functions default to 0.0 'cause
+- 463_: make timeout parameter of cpu_percent* functions default to 0.0 'cause
it's a common trap to introduce slowdowns.
-- #468: move documentation to readthedocs.com.
-- #477: process cpu_percent() is about 30% faster. (suggested by crusaderky)
-- #478: [Linux] almost all APIs are about 30% faster on Python 3.X.
-- #479: long deprecated psutil.error module is gone; exception classes now
+- 468_: move documentation to readthedocs.com.
+- 477_: process cpu_percent() is about 30% faster. (suggested by crusaderky)
+- 478_: [Linux] almost all APIs are about 30% faster on Python 3.X.
+- 479_: long deprecated psutil.error module is gone; exception classes now
live in "psutil" namespace only.
-**Bug fixes**
+Bug fixes
+---------
-- #193: psutil.Popen constructor can throw an exception if the spawned process
+- 193_: psutil.Popen constructor can throw an exception if the spawned process
terminates quickly.
-- #340: [Windows] process get_open_files() no longer hangs. (patch by
+- 340_: [Windows] process get_open_files() no longer hangs. (patch by
jtang@vahna.net)
-- #443: [Linux] fix a potential overflow issue for Process.set_cpu_affinity()
+- 443_: [Linux] fix a potential overflow issue for Process.set_cpu_affinity()
on systems with more than 64 CPUs.
-- #448: [Windows] get_children() and ppid() memory leak (patch by Ulrich
+- 448_: [Windows] get_children() and ppid() memory leak (patch by Ulrich
Klank).
-- #457: [POSIX] pid_exists() always returns True for PID 0.
-- #461: namedtuples are not pickle-able.
-- #466: [Linux] process exe improper null bytes handling. (patch by
+- 457_: [POSIX] pid_exists() always returns True for PID 0.
+- 461_: namedtuples are not pickle-able.
+- 466_: [Linux] process exe improper null bytes handling. (patch by
Gautam Singh)
-- #470: wait_procs() might not wait. (patch by crusaderky)
-- #471: [Windows] process exe improper unicode handling. (patch by
+- 470_: wait_procs() might not wait. (patch by crusaderky)
+- 471_: [Windows] process exe improper unicode handling. (patch by
alex@mroja.net)
-- #473: psutil.Popen.wait() does not set returncode attribute.
-- #474: [Windows] Process.cpu_percent() is no longer capped at 100%.
-- #476: [Linux] encoding error for process name and cmdline.
+- 473_: psutil.Popen.wait() does not set returncode attribute.
+- 474_: [Windows] Process.cpu_percent() is no longer capped at 100%.
+- 476_: [Linux] encoding error for process name and cmdline.
-**API changes**
+API changes
+-----------
For the sake of consistency a lot of psutil APIs have been renamed.
In most cases accessing the old names will work but it will cause a
@@ -603,117 +741,146 @@ DeprecationWarning.
been renamed to "returncode" for consistency with subprocess.Popen.
-1.2.1 - 2013-11-25
-==================
+1.2.1
+=====
+
+*2013-11-25*
-**Bug fixes**
+Bug fixes
+---------
-- #348: [Windows XP] fixed "ImportError: DLL load failed" occurring on module
+- 348_: [Windows XP] fixed "ImportError: DLL load failed" occurring on module
import.
-- #425: [Solaris] crash on import due to failure at determining BOOT_TIME.
-- #443: [Linux] can't set CPU affinity on systems with more than 64 cores.
+- 425_: [Solaris] crash on import due to failure at determining BOOT_TIME.
+- 443_: [Linux] can't set CPU affinity on systems with more than 64 cores.
-1.2.0 - 2013-11-20
-==================
+1.2.0
+=====
-**Enhancements**
+*2013-11-20*
-- #439: assume os.getpid() if no argument is passed to psutil.Process
+Enhancements
+------------
+
+- 439_: assume os.getpid() if no argument is passed to psutil.Process
constructor.
-- #440: new psutil.wait_procs() utility function which waits for multiple
+- 440_: new psutil.wait_procs() utility function which waits for multiple
processes to terminate.
-**Bug fixes**
+Bug fixes
+---------
-- #348: [Windows XP/Vista] fix "ImportError: DLL load failed" occurring on
+- 348_: [Windows XP/Vista] fix "ImportError: DLL load failed" occurring on
module import.
-1.1.3 - 2013-11-07
-==================
+1.1.3
+=====
+
+*2013-11-07*
-**Bug fixes**
+Bug fixes
+---------
-- #442: [Linux] psutil won't compile on certain version of Linux because of
+- 442_: [Linux] psutil won't compile on certain version of Linux because of
missing prlimit(2) syscall.
-1.1.2 - 2013-10-22
-==================
+1.1.2
+=====
-**Bug fixes**
+*2013-10-22*
-- #442: [Linux] psutil won't compile on Debian 6.0 because of missing
+Bug fixes
+---------
+
+- 442_: [Linux] psutil won't compile on Debian 6.0 because of missing
prlimit(2) syscall.
-1.1.1 - 2013-10-08
-==================
+1.1.1
+=====
+
+*2013-10-08*
-**Bug fixes**
+Bug fixes
+---------
-- #442: [Linux] psutil won't compile on kernels < 2.6.36 due to missing
+- 442_: [Linux] psutil won't compile on kernels < 2.6.36 due to missing
prlimit(2) syscall.
-1.1.0 - 2013-09-28
-==================
+1.1.0
+=====
-**Enhancements**
+*2013-09-28*
-- #410: host tar.gz and windows binary files are on PYPI.
-- #412: [Linux] get/set process resource limits.
-- #415: [Windows] Process.get_children() is an order of magnitude faster.
-- #426: [Windows] Process.name is an order of magnitude faster.
-- #431: [UNIX] Process.name is slightly faster because it unnecessarily
+Enhancements
+------------
+
+- 410_: host tar.gz and windows binary files are on PYPI.
+- 412_: [Linux] get/set process resource limits.
+- 415_: [Windows] Process.get_children() is an order of magnitude faster.
+- 426_: [Windows] Process.name is an order of magnitude faster.
+- 431_: [UNIX] Process.name is slightly faster because it unnecessarily
retrieved also process cmdline.
-**Bug fixes**
+Bug fixes
+---------
+
+- 391_: [Windows] psutil.cpu_times_percent() returns negative percentages.
+- 408_: STATUS_* and CONN_* constants don't properly serialize on JSON.
+- 411_: [Windows] examples/disk_usage.py may pop-up a GUI error.
+- 413_: [Windows] Process.get_memory_info() leaks memory.
+- 414_: [Windows] Process.exe on Windows XP may raise ERROR_INVALID_PARAMETER.
+- 416_: psutil.disk_usage() doesn't work well with unicode path names.
+- 430_: [Linux] process IO counters report wrong number of r/w syscalls.
+- 435_: [Linux] psutil.net_io_counters() might report erreneous NIC names.
+- 436_: [Linux] psutil.net_io_counters() reports a wrong 'dropin' value.
+
+API changes
+-----------
-- #391: [Windows] psutil.cpu_times_percent() returns negative percentages.
-- #408: STATUS_* and CONN_* constants don't properly serialize on JSON.
-- #411: [Windows] examples/disk_usage.py may pop-up a GUI error.
-- #413: [Windows] Process.get_memory_info() leaks memory.
-- #414: [Windows] Process.exe on Windows XP may raise ERROR_INVALID_PARAMETER.
-- #416: psutil.disk_usage() doesn't work well with unicode path names.
-- #430: [Linux] process IO counters report wrong number of r/w syscalls.
-- #435: [Linux] psutil.net_io_counters() might report erreneous NIC names.
-- #436: [Linux] psutil.net_io_counters() reports a wrong 'dropin' value.
+- 408_: turn STATUS_* and CONN_* constants into plain Python strings.
-**API changes**
-- #408: turn STATUS_* and CONN_* constants into plain Python strings.
+1.0.1
+=====
+*2013-07-12*
-1.0.1 - 2013-07-12
-==================
+Bug fixes
+---------
-**Bug fixes**
+- 405_: network_io_counters(pernic=True) no longer works as intended in 1.0.0.
-- #405: network_io_counters(pernic=True) no longer works as intended in 1.0.0.
+1.0.0
+=====
-1.0.0 - 2013-07-10
-==================
+*2013-07-10*
-**Enhancements**
+Enhancements
+------------
-- #18: Solaris support (yay!) (thanks Justin Venus)
-- #367: Process.get_connections() 'status' strings are now constants.
-- #380: test suite exits with non-zero on failure. (patch by floppymaster)
-- #391: introduce unittest2 facilities and provide workarounds if unittest2
+- 18_: Solaris support (yay!) (thanks Justin Venus)
+- 367_: Process.get_connections() 'status' strings are now constants.
+- 380_: test suite exits with non-zero on failure. (patch by floppymaster)
+- 391_: introduce unittest2 facilities and provide workarounds if unittest2
is not installed (python < 2.7).
-**Bug fixes**
+Bug fixes
+---------
-- #374: [Windows] negative memory usage reported if process uses a lot of
+- 374_: [Windows] negative memory usage reported if process uses a lot of
memory.
-- #379: [Linux] Process.get_memory_maps() may raise ValueError.
-- #394: [OSX] Mapped memory regions report incorrect file name.
-- #404: [Linux] sched_*affinity() are implicitly declared. (patch by Arfrever)
+- 379_: [Linux] Process.get_memory_maps() may raise ValueError.
+- 394_: [OSX] Mapped memory regions report incorrect file name.
+- 404_: [Linux] sched_*affinity() are implicitly declared. (patch by Arfrever)
-**API changes**
+API changes
+-----------
- Process.get_connections() 'status' field is no longer a string but a
constant object (psutil.CONN_*).
@@ -722,72 +889,80 @@ DeprecationWarning.
- psutil.network_io_counters() renamed to psutil.net_io_counters().
-0.7.1 - 2013-05-03
-==================
+0.7.1
+=====
-**Bug fixes**
+*2013-05-03*
-- #325: [BSD] psutil.virtual_memory() can raise SystemError.
+Bug fixes
+---------
+
+- 325_: [BSD] psutil.virtual_memory() can raise SystemError.
(patch by Jan Beich)
-- #370: [BSD] Process.get_connections() requires root. (patch by John Baldwin)
-- #372: [BSD] different process methods raise NoSuchProcess instead of
+- 370_: [BSD] Process.get_connections() requires root. (patch by John Baldwin)
+- 372_: [BSD] different process methods raise NoSuchProcess instead of
AccessDenied.
-0.7.0 - 2013-04-12
-==================
+0.7.0
+=====
+
+*2013-04-12*
-**Enhancements**
+Enhancements
+------------
-- #233: code migrated to Mercurial (yay!)
-- #246: psutil.error module is deprecated and scheduled for removal.
-- #328: [Windows] process IO nice/priority support.
-- #359: psutil.get_boot_time()
-- #361: [Linux] psutil.cpu_times() now includes new 'steal', 'guest' and
+- 233_: code migrated to Mercurial (yay!)
+- 246_: psutil.error module is deprecated and scheduled for removal.
+- 328_: [Windows] process IO nice/priority support.
+- 359_: psutil.get_boot_time()
+- 361_: [Linux] psutil.cpu_times() now includes new 'steal', 'guest' and
'guest_nice' fields available on recent Linux kernels.
Also, psutil.cpu_percent() is more accurate.
-- #362: cpu_times_percent() (per-CPU-time utilization as a percentage)
+- 362_: cpu_times_percent() (per-CPU-time utilization as a percentage)
-**Bug fixes**
+Bug fixes
+---------
-- #234: [Windows] disk_io_counters() fails to list certain disks.
-- #264: [Windows] use of psutil.disk_partitions() may cause a message box to
+- 234_: [Windows] disk_io_counters() fails to list certain disks.
+- 264_: [Windows] use of psutil.disk_partitions() may cause a message box to
appear.
-- #313: [Linux] psutil.virtual_memory() and psutil.swap_memory() can crash on
+- 313_: [Linux] psutil.virtual_memory() and psutil.swap_memory() can crash on
certain exotic Linux flavors having an incomplete /proc interface.
If that's the case we now set the unretrievable stats to 0 and raise a
RuntimeWarning.
-- #315: [OSX] fix some compilation warnings.
-- #317: [Windows] cannot set process CPU affinity above 31 cores.
-- #319: [Linux] process get_memory_maps() raises KeyError 'Anonymous' on Debian
+- 315_: [OSX] fix some compilation warnings.
+- 317_: [Windows] cannot set process CPU affinity above 31 cores.
+- 319_: [Linux] process get_memory_maps() raises KeyError 'Anonymous' on Debian
squeeze.
-- #321: [UNIX] Process.ppid property is no longer cached as the kernel may set
+- 321_: [UNIX] Process.ppid property is no longer cached as the kernel may set
the ppid to 1 in case of a zombie process.
-- #323: [OSX] disk_io_counters()'s read_time and write_time parameters were
+- 323_: [OSX] disk_io_counters()'s read_time and write_time parameters were
reporting microseconds not milliseconds. (patch by Gregory Szorc)
-- #331: Process cmdline is no longer cached after first acces as it may change.
-- #333: [OSX] Leak of Mach ports on OS X (patch by rsesek@google.com)
-- #337: [Linux] process methods not working because of a poor /proc
+- 331_: Process cmdline is no longer cached after first acces as it may change.
+- 333_: [OSX] Leak of Mach ports on OS X (patch by rsesek@google.com)
+- 337_: [Linux] process methods not working because of a poor /proc
implementation will raise NotImplementedError rather than RuntimeError
and Process.as_dict() will not blow up. (patch by Curtin1060)
-- #338: [Linux] disk_io_counters() fails to find some disks.
-- #339: [FreeBSD] get_pid_list() can allocate all the memory on system.
-- #341: [Linux] psutil might crash on import due to error in retrieving system
+- 338_: [Linux] disk_io_counters() fails to find some disks.
+- 339_: [FreeBSD] get_pid_list() can allocate all the memory on system.
+- 341_: [Linux] psutil might crash on import due to error in retrieving system
terminals map.
-- #344: [FreeBSD] swap_memory() might return incorrect results due to
+- 344_: [FreeBSD] swap_memory() might return incorrect results due to
kvm_open(3) not being called. (patch by Jean Sebastien)
-- #338: [Linux] disk_io_counters() fails to find some disks.
-- #351: [Windows] if psutil is compiled with mingw32 (provided installers for
+- 338_: [Linux] disk_io_counters() fails to find some disks.
+- 351_: [Windows] if psutil is compiled with mingw32 (provided installers for
py2.4 and py2.5 are) disk_io_counters() will fail. (Patch by m.malycha)
-- #353: [OSX] get_users() returns an empty list on OSX 10.8.
-- #356: Process.parent now checks whether parent PID has been reused in which
+- 353_: [OSX] get_users() returns an empty list on OSX 10.8.
+- 356_: Process.parent now checks whether parent PID has been reused in which
case returns None.
-- #365: Process.set_nice() should check PID has not been reused by another
+- 365_: Process.set_nice() should check PID has not been reused by another
process.
-- #366: [FreeBSD] get_memory_maps(), get_num_fds(), get_open_files() and
+- 366_: [FreeBSD] get_memory_maps(), get_num_fds(), get_open_files() and
getcwd() Process methods raise RuntimeError instead of AccessDenied.
-**API changes**
+API changes
+-----------
- Process.cmdline property is no longer cached after first access.
- Process.ppid property is no longer cached after first access.
@@ -796,38 +971,46 @@ DeprecationWarning.
- psutil.error module is deprecated and scheduled for removal.
-0.6.1 - 2012-08-16
-==================
+0.6.1
+=====
-**Enhancements**
+*2012-08-16*
-- #316: process cmdline property now makes a better job at guessing the process
+Enhancements
+------------
+
+- 316_: process cmdline property now makes a better job at guessing the process
executable from the cmdline.
-**Bug fixes**
+Bug fixes
+---------
-- #316: process exe was resolved in case it was a symlink.
-- #318: python 2.4 compatibility was broken.
+- 316_: process exe was resolved in case it was a symlink.
+- 318_: python 2.4 compatibility was broken.
-**API changes**
+API changes
+-----------
- process exe can now return an empty string instead of raising AccessDenied.
- process exe is no longer resolved in case it's a symlink.
-0.6.0 - 2012-08-13
-==================
+0.6.0
+=====
+
+*2012-08-13*
-**Enhancements**
+Enhancements
+------------
-- #216: [POSIX] get_connections() UNIX sockets support.
-- #220: [FreeBSD] get_connections() has been rewritten in C and no longer
+- 216_: [POSIX] get_connections() UNIX sockets support.
+- 220_: [FreeBSD] get_connections() has been rewritten in C and no longer
requires lsof.
-- #222: [OSX] add support for process cwd.
-- #261: process extended memory info.
-- #295: [OSX] process executable path is now determined by asking the OS
+- 222_: [OSX] add support for process cwd.
+- 261_: process extended memory info.
+- 295_: [OSX] process executable path is now determined by asking the OS
instead of being guessed from process cmdline.
-- #297: [OSX] the Process methods below were always raising AccessDenied for
+- 297_: [OSX] the Process methods below were always raising AccessDenied for
any process except the current one. Now this is no longer true. Also
they are 2.5x faster.
- name
@@ -836,10 +1019,10 @@ DeprecationWarning.
- get_cpu_times()
- get_cpu_percent()
- get_num_threads()
-- #300: examples/pmap.py script.
-- #301: process_iter() now yields processes sorted by their PIDs.
-- #302: process number of voluntary and involuntary context switches.
-- #303: [Windows] the Process methods below were always raising AccessDenied
+- 300_: examples/pmap.py script.
+- 301_: process_iter() now yields processes sorted by their PIDs.
+- 302_: process number of voluntary and involuntary context switches.
+- 303_: [Windows] the Process methods below were always raising AccessDenied
for any process not owned by current user. Now this is no longer true:
- create_time
- get_cpu_times()
@@ -848,8 +1031,8 @@ DeprecationWarning.
- get_memory_percent()
- get_num_handles()
- get_io_counters()
-- #305: add examples/netstat.py script.
-- #311: system memory functions has been refactorized and rewritten and now
+- 305_: add examples/netstat.py script.
+- 311_: system memory functions has been refactorized and rewritten and now
provide a more detailed and consistent representation of the system
memory. New psutil.virtual_memory() function provides the following
memory amounts:
@@ -872,28 +1055,30 @@ DeprecationWarning.
- sout (no. of bytes the system has swapped out from disk (cumulative))
All old memory-related functions are deprecated.
Also two new example scripts were added: free.py and meminfo.py.
-- #312: psutil.network_io_counters() namedtuple includes 4 new fields:
+- 312_: psutil.network_io_counters() namedtuple includes 4 new fields:
errin, errout dropin and dropout, reflecting the number of packets
dropped and with errors.
-**Bugfixes**
+Bugfixes
+--------
-- #298: [OSX and BSD] memory leak in get_num_fds().
-- #299: potential memory leak every time PyList_New(0) is used.
-- #303: [Windows] potential heap corruption in get_num_threads() and
+- 298_: [OSX and BSD] memory leak in get_num_fds().
+- 299_: potential memory leak every time PyList_New(0) is used.
+- 303_: [Windows] potential heap corruption in get_num_threads() and
get_status() Process methods.
-- #305: [FreeBSD] psutil can't compile on FreeBSD 9 due to removal of utmp.h.
-- #306: at C level, errors are not checked when invoking Py* functions which
+- 305_: [FreeBSD] psutil can't compile on FreeBSD 9 due to removal of utmp.h.
+- 306_: at C level, errors are not checked when invoking Py* functions which
create or manipulate Python objects leading to potential memory related
errors and/or segmentation faults.
-- #307: [FreeBSD] values returned by psutil.network_io_counters() are wrong.
-- #308: [BSD / Windows] psutil.virtmem_usage() wasn't actually returning
+- 307_: [FreeBSD] values returned by psutil.network_io_counters() are wrong.
+- 308_: [BSD / Windows] psutil.virtmem_usage() wasn't actually returning
information about swap memory usage as it was supposed to do. It does
now.
-- #309: get_open_files() might not return files which can not be accessed
+- 309_: get_open_files() might not return files which can not be accessed
due to limited permissions. AccessDenied is now raised instead.
-**API changes**
+API changes
+-----------
- psutil.phymem_usage() is deprecated (use psutil.virtual_memory())
- psutil.virtmem_usage() is deprecated (use psutil.swap_memory())
@@ -903,78 +1088,87 @@ DeprecationWarning.
memory instead of virtual memory.
-0.5.1 - 2012-06-29
-==================
+0.5.1
+=====
-**Enhancements**
+*2012-06-29*
-- #293: [Windows] process executable path is now determined by asking the OS
+Enhancements
+------------
+
+- 293_: [Windows] process executable path is now determined by asking the OS
instead of being guessed from process cmdline.
-**Bugfixes**
+Bugfixes
+--------
+
+- 292_: [Linux] race condition in process files/threads/connections.
+- 294_: [Windows] Process CPU affinity is only able to set CPU #0.
-- #292: [Linux] race condition in process files/threads/connections.
-- #294: [Windows] Process CPU affinity is only able to set CPU #0.
+0.5.0
+=====
-0.5.0 - 2012-06-27
-==================
+*2012-06-27*
-**Enhancements**
+Enhancements
+------------
-- #195: [Windows] number of handles opened by process.
-- #209: psutil.disk_partitions() now provides also mount options.
-- #229: list users currently connected on the system (psutil.get_users()).
-- #238: [Linux, Windows] process CPU affinity (get and set).
-- #242: Process.get_children(recursive=True): return all process
+- 195_: [Windows] number of handles opened by process.
+- 209_: psutil.disk_partitions() now provides also mount options.
+- 229_: list users currently connected on the system (psutil.get_users()).
+- 238_: [Linux, Windows] process CPU affinity (get and set).
+- 242_: Process.get_children(recursive=True): return all process
descendants.
-- #245: [POSIX] Process.wait() incrementally consumes less CPU cycles.
-- #257: [Windows] removed Windows 2000 support.
-- #258: [Linux] Process.get_memory_info() is now 0.5x faster.
-- #260: process's mapped memory regions. (Windows patch by wj32.64, OSX patch
+- 245_: [POSIX] Process.wait() incrementally consumes less CPU cycles.
+- 257_: [Windows] removed Windows 2000 support.
+- 258_: [Linux] Process.get_memory_info() is now 0.5x faster.
+- 260_: process's mapped memory regions. (Windows patch by wj32.64, OSX patch
by Jeremy Whitlock)
-- #262: [Windows] psutil.disk_partitions() was slow due to inspecting the
+- 262_: [Windows] psutil.disk_partitions() was slow due to inspecting the
floppy disk drive also when "all" argument was False.
-- #273: psutil.get_process_list() is deprecated.
-- #274: psutil no longer requires 2to3 at installation time in order to work
+- 273_: psutil.get_process_list() is deprecated.
+- 274_: psutil no longer requires 2to3 at installation time in order to work
with Python 3.
-- #278: new Process.as_dict() method.
-- #281: ppid, name, exe, cmdline and create_time properties of Process class
+- 278_: new Process.as_dict() method.
+- 281_: ppid, name, exe, cmdline and create_time properties of Process class
are now cached after being accessed.
-- #282: psutil.STATUS_* constants can now be compared by using their string
+- 282_: psutil.STATUS_* constants can now be compared by using their string
representation.
-- #283: speedup Process.is_running() by caching its return value in case the
+- 283_: speedup Process.is_running() by caching its return value in case the
process is terminated.
-- #284: [POSIX] per-process number of opened file descriptors.
-- #287: psutil.process_iter() now caches Process instances between calls.
-- #290: Process.nice property is deprecated in favor of new get_nice() and
+- 284_: [POSIX] per-process number of opened file descriptors.
+- 287_: psutil.process_iter() now caches Process instances between calls.
+- 290_: Process.nice property is deprecated in favor of new get_nice() and
set_nice() methods.
-**Bugfixes**
+Bugfixes
+--------
-- #193: psutil.Popen constructor can throw an exception if the spawned process
+- 193_: psutil.Popen constructor can throw an exception if the spawned process
terminates quickly.
-- #240: [OSX] incorrect use of free() for Process.get_connections().
-- #244: [POSIX] Process.wait() can hog CPU resources if called against a
+- 240_: [OSX] incorrect use of free() for Process.get_connections().
+- 244_: [POSIX] Process.wait() can hog CPU resources if called against a
process which is not our children.
-- #248: [Linux] psutil.network_io_counters() might return erroneous NIC names.
-- #252: [Windows] process getcwd() erroneously raise NoSuchProcess for
+- 248_: [Linux] psutil.network_io_counters() might return erroneous NIC names.
+- 252_: [Windows] process getcwd() erroneously raise NoSuchProcess for
processes owned by another user. It now raises AccessDenied instead.
-- #266: [Windows] psutil.get_pid_list() only shows 1024 processes.
+- 266_: [Windows] psutil.get_pid_list() only shows 1024 processes.
(patch by Amoser)
-- #267: [OSX] Process.get_connections() - an erroneous remote address was
+- 267_: [OSX] Process.get_connections() - an erroneous remote address was
returned. (Patch by Amoser)
-- #272: [Linux] Porcess.get_open_files() - potential race condition can lead to
+- 272_: [Linux] Porcess.get_open_files() - potential race condition can lead to
unexpected NoSuchProcess exception. Also, we can get incorrect reports
of not absolutized path names.
-- #275: [Linux] Process.get_io_counters() erroneously raise NoSuchProcess on
+- 275_: [Linux] Process.get_io_counters() erroneously raise NoSuchProcess on
old Linux versions. Where not available it now raises
NotImplementedError.
-- #286: Process.is_running() doesn't actually check whether PID has been
+- 286_: Process.is_running() doesn't actually check whether PID has been
reused.
-- #314: Process.get_children() can sometimes return non-children.
+- 314_: Process.get_children() can sometimes return non-children.
-**API changes**
+API changes
+-----------
- Process.nice property is deprecated in favor of new get_nice() and set_nice()
methods.
@@ -986,171 +1180,192 @@ DeprecationWarning.
representation.
-0.4.1 - 2011-12-14
-==================
+0.4.1
+=====
+
+*2011-12-14*
-**Bugfixes**
+Bugfixes
+--------
-- #228: some example scripts were not working with python 3.
-- #230: [Windows / OSX] memory leak in Process.get_connections().
-- #232: [Linux] psutil.phymem_usage() can report erroneous values which are
+- 228_: some example scripts were not working with python 3.
+- 230_: [Windows / OSX] memory leak in Process.get_connections().
+- 232_: [Linux] psutil.phymem_usage() can report erroneous values which are
different than "free" command.
-- #236: [Windows] memory/handle leak in Process's get_memory_info(),
+- 236_: [Windows] memory/handle leak in Process's get_memory_info(),
suspend() and resume() methods.
-0.4.0 - 2011-10-29
-==================
+0.4.0
+=====
-**Enhancements**
+*2011-10-29*
-- #150: network I/O counters. (OSX and Windows patch by Jeremy Whitlock)
-- #154: [FreeBSD] add support for process getcwd()
-- #157: [Windows] provide installer for Python 3.2 64-bit.
-- #198: Process.wait(timeout=0) can now be used to make wait() return
+Enhancements
+------------
+
+- 150_: network I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+- 154_: [FreeBSD] add support for process getcwd()
+- 157_: [Windows] provide installer for Python 3.2 64-bit.
+- 198_: Process.wait(timeout=0) can now be used to make wait() return
immediately.
-- #206: disk I/O counters. (OSX and Windows patch by Jeremy Whitlock)
-- #213: examples/iotop.py script.
-- #217: Process.get_connections() now has a "kind" argument to filter
+- 206_: disk I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+- 213_: examples/iotop.py script.
+- 217_: Process.get_connections() now has a "kind" argument to filter
for connections with different criteria.
-- #221: [FreeBSD] Process.get_open_files has been rewritten in C and no longer
+- 221_: [FreeBSD] Process.get_open_files has been rewritten in C and no longer
relies on lsof.
-- #223: examples/top.py script.
-- #227: examples/nettop.py script.
+- 223_: examples/top.py script.
+- 227_: examples/nettop.py script.
-**Bugfixes**
+Bugfixes
+--------
-- #135: [OSX] psutil cannot create Process object.
-- #144: [Linux] no longer support 0 special PID.
-- #188: [Linux] psutil import error on Linux ARM architectures.
-- #194: [POSIX] psutil.Process.get_cpu_percent() now reports a percentage over
+- 135_: [OSX] psutil cannot create Process object.
+- 144_: [Linux] no longer support 0 special PID.
+- 188_: [Linux] psutil import error on Linux ARM architectures.
+- 194_: [POSIX] psutil.Process.get_cpu_percent() now reports a percentage over
100 on multicore processors.
-- #197: [Linux] Process.get_connections() is broken on platforms not
+- 197_: [Linux] Process.get_connections() is broken on platforms not
supporting IPv6.
-- #200: [Linux] psutil.NUM_CPUS not working on armel and sparc architectures
+- 200_: [Linux] psutil.NUM_CPUS not working on armel and sparc architectures
and causing crash on module import.
-- #201: [Linux] Process.get_connections() is broken on big-endian
+- 201_: [Linux] Process.get_connections() is broken on big-endian
architectures.
-- #211: Process instance can unexpectedly raise NoSuchProcess if tested for
+- 211_: Process instance can unexpectedly raise NoSuchProcess if tested for
equality with another object.
-- #218: [Linux] crash at import time on Debian 64-bit because of a missing
+- 218_: [Linux] crash at import time on Debian 64-bit because of a missing
line in /proc/meminfo.
-- #226: [FreeBSD] crash at import time on FreeBSD 7 and minor.
+- 226_: [FreeBSD] crash at import time on FreeBSD 7 and minor.
+
+0.3.0
+=====
-0.3.0 - 2011-07-08
-==================
+*2011-07-08*
-**Enhancements**
+Enhancements
+------------
-- #125: system per-cpu percentage utilization and times.
-- #163: per-process associated terminal (TTY).
-- #171: added get_phymem() and get_virtmem() functions returning system
+- 125_: system per-cpu percentage utilization and times.
+- 163_: per-process associated terminal (TTY).
+- 171_: added get_phymem() and get_virtmem() functions returning system
memory information (total, used, free) and memory percent usage.
total_* avail_* and used_* memory functions are deprecated.
-- #172: disk usage statistics.
-- #174: mounted disk partitions.
-- #179: setuptools is now used in setup.py
+- 172_: disk usage statistics.
+- 174_: mounted disk partitions.
+- 179_: setuptools is now used in setup.py
-**Bugfixes**
+Bugfixes
+--------
-- #159: SetSeDebug() does not close handles or unset impersonation on return.
-- #164: [Windows] wait function raises a TimeoutException when a process
+- 159_: SetSeDebug() does not close handles or unset impersonation on return.
+- 164_: [Windows] wait function raises a TimeoutException when a process
returns -1 .
-- #165: process.status raises an unhandled exception.
-- #166: get_memory_info() leaks handles hogging system resources.
-- #168: psutil.cpu_percent() returns erroneous results when used in
+- 165_: process.status raises an unhandled exception.
+- 166_: get_memory_info() leaks handles hogging system resources.
+- 168_: psutil.cpu_percent() returns erroneous results when used in
non-blocking mode. (patch by Philip Roberts)
-- #178: OSX - Process.get_threads() leaks memory
-- #180: [Windows] Process's get_num_threads() and get_threads() methods can
+- 178_: OSX - Process.get_threads() leaks memory
+- 180_: [Windows] Process's get_num_threads() and get_threads() methods can
raise NoSuchProcess exception while process still exists.
-0.2.1 - 2011-03-20
-==================
+0.2.1
+=====
-**Enhancements**
+*2011-03-20*
-- #64: per-process I/O counters.
-- #116: per-process wait() (wait for process to terminate and return its exit
+Enhancements
+------------
+
+- 64_: per-process I/O counters.
+- 116_: per-process wait() (wait for process to terminate and return its exit
code).
-- #134: per-process get_threads() returning information (id, user and kernel
+- 134_: per-process get_threads() returning information (id, user and kernel
times) about threads opened by process.
-- #136: process executable path on FreeBSD is now determined by asking the
+- 136_: process executable path on FreeBSD is now determined by asking the
kernel instead of guessing it from cmdline[0].
-- #137: per-process real, effective and saved user and group ids.
-- #140: system boot time.
-- #142: per-process get and set niceness (priority).
-- #143: per-process status.
-- #147: per-process I/O nice (priority) - Linux only.
-- #148: psutil.Popen class which tidies up subprocess.Popen and psutil.Process
+- 137_: per-process real, effective and saved user and group ids.
+- 140_: system boot time.
+- 142_: per-process get and set niceness (priority).
+- 143_: per-process status.
+- 147_: per-process I/O nice (priority) - Linux only.
+- 148_: psutil.Popen class which tidies up subprocess.Popen and psutil.Process
in a unique interface.
-- #152: [OSX] get_process_open_files() implementation has been rewritten
+- 152_: [OSX] get_process_open_files() implementation has been rewritten
in C and no longer relies on lsof resulting in a 3x speedup.
-- #153: [OSX] get_process_connection() implementation has been rewritten
+- 153_: [OSX] get_process_connection() implementation has been rewritten
in C and no longer relies on lsof resulting in a 3x speedup.
-**Bugfixes**
+Bugfixes
+--------
-- #83: process cmdline is empty on OSX 64-bit.
-- #130: a race condition can cause IOError exception be raised on
+- 83_: process cmdline is empty on OSX 64-bit.
+- 130_: a race condition can cause IOError exception be raised on
Linux if process disappears between open() and subsequent read() calls.
-- #145: WindowsError was raised instead of psutil.AccessDenied when using
+- 145_: WindowsError was raised instead of psutil.AccessDenied when using
process resume() or suspend() on Windows.
-- #146: 'exe' property on Linux can raise TypeError if path contains NULL
+- 146_: 'exe' property on Linux can raise TypeError if path contains NULL
bytes.
-- #151: exe and getcwd() for PID 0 on Linux return inconsistent data.
+- 151_: exe and getcwd() for PID 0 on Linux return inconsistent data.
-**API changes**
+API changes
+-----------
- Process "uid" and "gid" properties are deprecated in favor of "uids" and
"gids" properties.
-0.2.0 - 2010-11-13
-==================
+0.2.0
+=====
+
+*2010-11-13*
-**Enhancements**
+Enhancements
+------------
-- #79: per-process open files.
-- #88: total system physical cached memory.
-- #88: total system physical memory buffers used by the kernel.
-- #91: per-process send_signal() and terminate() methods.
-- #95: NoSuchProcess and AccessDenied exception classes now provide "pid",
+- 79_: per-process open files.
+- 88_: total system physical cached memory.
+- 88_: total system physical memory buffers used by the kernel.
+- 91_: per-process send_signal() and terminate() methods.
+- 95_: NoSuchProcess and AccessDenied exception classes now provide "pid",
"name" and "msg" attributes.
-- #97: per-process children.
-- #98: Process.get_cpu_times() and Process.get_memory_info now return
+- 97_: per-process children.
+- 98_: Process.get_cpu_times() and Process.get_memory_info now return
a namedtuple instead of a tuple.
-- #103: per-process opened TCP and UDP connections.
-- #107: add support for Windows 64 bit. (patch by cjgohlke)
-- #111: per-process executable name.
-- #113: exception messages now include process name and pid.
-- #114: process username Windows implementation has been rewritten in pure
+- 103_: per-process opened TCP and UDP connections.
+- 107_: add support for Windows 64 bit. (patch by cjgohlke)
+- 111_: per-process executable name.
+- 113_: exception messages now include process name and pid.
+- 114_: process username Windows implementation has been rewritten in pure
C and no longer uses WMI resulting in a big speedup. Also, pywin32 is no
longer required as a third-party dependancy. (patch by wj32)
-- #117: added support for Windows 2000.
-- #123: psutil.cpu_percent() and psutil.Process.cpu_percent() accept a
+- 117_: added support for Windows 2000.
+- 123_: psutil.cpu_percent() and psutil.Process.cpu_percent() accept a
new 'interval' parameter.
-- #129: per-process number of threads.
+- 129_: per-process number of threads.
-**Bugfixes**
+Bugfixes
+--------
-- #80: fixed warnings when installing psutil with easy_install.
-- #81: psutil fails to compile with Visual Studio.
-- #94: suspend() raises OSError instead of AccessDenied.
-- #86: psutil didn't compile against FreeBSD 6.x.
-- #102: orphaned process handles obtained by using OpenProcess in C were
+- 80_: fixed warnings when installing psutil with easy_install.
+- 81_: psutil fails to compile with Visual Studio.
+- 94_: suspend() raises OSError instead of AccessDenied.
+- 86_: psutil didn't compile against FreeBSD 6.x.
+- 102_: orphaned process handles obtained by using OpenProcess in C were
left behind every time Process class was instantiated.
-- #111: path and name Process properties report truncated or erroneous
+- 111_: path and name Process properties report truncated or erroneous
values on UNIX.
-- #120: cpu_percent() always returning 100% on OS X.
-- #112: uid and gid properties don't change if process changes effective
+- 120_: cpu_percent() always returning 100% on OS X.
+- 112_: uid and gid properties don't change if process changes effective
user/group id at some point.
-- #126: ppid, uid, gid, name, exe, cmdline and create_time properties are
+- 126_: ppid, uid, gid, name, exe, cmdline and create_time properties are
no longer cached and correctly raise NoSuchProcess exception if the process
disappears.
-**API changes**
+API changes
+-----------
- psutil.Process.path property is deprecated and works as an alias for "exe"
property.
@@ -1167,86 +1382,1098 @@ DeprecationWarning.
immediately by default (see issue 123).
-0.1.3 - 2010-03-02
-==================
+0.1.3
+=====
-**Enhancements**
+*2010-03-02*
-- #14: per-process username
-- #51: per-process current working directory (Windows and Linux only)
-- #59: Process.is_running() is now 10 times faster
-- #61: added supoprt for FreeBSD 64 bit
-- #71: implemented suspend/resume process
-- #75: python 3 support
+Enhancements
+------------
-**Bugfixes**
+- 14_: per-process username
+- 51_: per-process current working directory (Windows and Linux only)
+- 59_: Process.is_running() is now 10 times faster
+- 61_: added supoprt for FreeBSD 64 bit
+- 71_: implemented suspend/resume process
+- 75_: python 3 support
-- #36: process cpu_times() and memory_info() functions succeeded also for dead
+Bugfixes
+--------
+
+- 36_: process cpu_times() and memory_info() functions succeeded also for dead
processes while a NoSuchProcess exception is supposed to be raised.
-- #48: incorrect size for mib array defined in getcmdargs for BSD
-- #49: possible memory leak due to missing free() on error condition on
-- #50: fixed getcmdargs() memory fragmentation on BSD
-- #55: test_pid_4 was failing on Windows Vista
-- #57: some unit tests were failing on systems where no swap memory is
+- 48_: incorrect size for mib array defined in getcmdargs for BSD
+- 49_: possible memory leak due to missing free() on error condition on
+- 50_: fixed getcmdargs() memory fragmentation on BSD
+- 55_: test_pid_4 was failing on Windows Vista
+- 57_: some unit tests were failing on systems where no swap memory is
available
-- #58: is_running() is now called before kill() to make sure we are going
+- 58_: is_running() is now called before kill() to make sure we are going
to kill the correct process.
-- #73: virtual memory size reported on OS X includes shared library size
-- #77: NoSuchProcess wasn't raised on Process.create_time if kill() was
+- 73_: virtual memory size reported on OS X includes shared library size
+- 77_: NoSuchProcess wasn't raised on Process.create_time if kill() was
used first.
-0.1.2 - 2009-05-06
-==================
+0.1.2
+=====
+
+*2009-05-06*
+
+Enhancements
+------------
-**Enhancements**
+- 32_: Per-process CPU user/kernel times
+- 33_: Process create time
+- 34_: Per-process CPU utilization percentage
+- 38_: Per-process memory usage (bytes)
+- 41_: Per-process memory utilization (percent)
+- 39_: System uptime
+- 43_: Total system virtual memory
+- 46_: Total system physical memory
+- 44_: Total system used/free virtual and physical memory
-- #32: Per-process CPU user/kernel times
-- #33: Process create time
-- #34: Per-process CPU utilization percentage
-- #38: Per-process memory usage (bytes)
-- #41: Per-process memory utilization (percent)
-- #39: System uptime
-- #43: Total system virtual memory
-- #46: Total system physical memory
-- #44: Total system used/free virtual and physical memory
+Bugfixes
+--------
-**Bugfixes**
+- 36_: [Windows] NoSuchProcess not raised when accessing timing methods.
+- 40_: test_get_cpu_times() failing on FreeBSD and OS X.
+- 42_: [Windows] get_memory_percent() raises AccessDenied.
-- #36: [Windows] NoSuchProcess not raised when accessing timing methods.
-- #40: test_get_cpu_times() failing on FreeBSD and OS X.
-- #42: [Windows] get_memory_percent() raises AccessDenied.
+0.1.1
+=====
-0.1.1 - 2009-03-06
-==================
+*2009-03-06*
-**Enhancements**
+Enhancements
+------------
-- #4: FreeBSD support for all functions of psutil
-- #9: Process.uid and Process.gid now retrieve process UID and GID.
-- #11: Support for parent/ppid - Process.parent property returns a
+- 4_: FreeBSD support for all functions of psutil
+- 9_: Process.uid and Process.gid now retrieve process UID and GID.
+- 11_: Support for parent/ppid - Process.parent property returns a
Process object representing the parent process, and Process.ppid returns
the parent PID.
-- #12 & 15:
+- 12_ & 15:
NoSuchProcess exception now raised when creating an object
for a nonexistent process, or when retrieving information about a process
that has gone away.
-- #21: AccessDenied exception created for raising access denied errors
+- 21_: AccessDenied exception created for raising access denied errors
from OSError or WindowsError on individual platforms.
-- #26: psutil.process_iter() function to iterate over processes as
+- 26_: psutil.process_iter() function to iterate over processes as
Process objects with a generator.
-- #?: Process objects can now also be compared with == operator for equality
+- Process objects can now also be compared with == operator for equality
(PID, name, command line are compared).
-**Bugfixes**
+Bugfixes
+--------
-- #16: [Windows] Special case for "System Idle Process" (PID 0) which
+- 16_: [Windows] Special case for "System Idle Process" (PID 0) which
otherwise would return an "invalid parameter" exception.
-- #17: get_process_list() ignores NoSuchProcess and AccessDenied
+- 17_: get_process_list() ignores NoSuchProcess and AccessDenied
exceptions during building of the list.
-- #22: [Windows] Process(0).kill() was failing with an unset exception.
-- #23: Special case for pid_exists(0)
-- #24: [Windows] Process(0).kill() now raises AccessDenied exception instead
+- 22_: [Windows] Process(0).kill() was failing with an unset exception.
+- 23_: Special case for pid_exists(0)
+- 24_: [Windows] Process(0).kill() now raises AccessDenied exception instead
of WindowsError.
-- #30: psutil.get_pid_list() was returning two ins
+- 30_: psutil.get_pid_list() was returning two ins
+
+.. _1: https://github.com/giampaolo/psutil/issues/1
+.. _2: https://github.com/giampaolo/psutil/issues/2
+.. _3: https://github.com/giampaolo/psutil/issues/3
+.. _4: https://github.com/giampaolo/psutil/issues/4
+.. _5: https://github.com/giampaolo/psutil/issues/5
+.. _6: https://github.com/giampaolo/psutil/issues/6
+.. _7: https://github.com/giampaolo/psutil/issues/7
+.. _8: https://github.com/giampaolo/psutil/issues/8
+.. _9: https://github.com/giampaolo/psutil/issues/9
+.. _10: https://github.com/giampaolo/psutil/issues/10
+.. _11: https://github.com/giampaolo/psutil/issues/11
+.. _12: https://github.com/giampaolo/psutil/issues/12
+.. _13: https://github.com/giampaolo/psutil/issues/13
+.. _14: https://github.com/giampaolo/psutil/issues/14
+.. _15: https://github.com/giampaolo/psutil/issues/15
+.. _16: https://github.com/giampaolo/psutil/issues/16
+.. _17: https://github.com/giampaolo/psutil/issues/17
+.. _18: https://github.com/giampaolo/psutil/issues/18
+.. _19: https://github.com/giampaolo/psutil/issues/19
+.. _20: https://github.com/giampaolo/psutil/issues/20
+.. _21: https://github.com/giampaolo/psutil/issues/21
+.. _22: https://github.com/giampaolo/psutil/issues/22
+.. _23: https://github.com/giampaolo/psutil/issues/23
+.. _24: https://github.com/giampaolo/psutil/issues/24
+.. _25: https://github.com/giampaolo/psutil/issues/25
+.. _26: https://github.com/giampaolo/psutil/issues/26
+.. _27: https://github.com/giampaolo/psutil/issues/27
+.. _28: https://github.com/giampaolo/psutil/issues/28
+.. _29: https://github.com/giampaolo/psutil/issues/29
+.. _30: https://github.com/giampaolo/psutil/issues/30
+.. _31: https://github.com/giampaolo/psutil/issues/31
+.. _32: https://github.com/giampaolo/psutil/issues/32
+.. _33: https://github.com/giampaolo/psutil/issues/33
+.. _34: https://github.com/giampaolo/psutil/issues/34
+.. _35: https://github.com/giampaolo/psutil/issues/35
+.. _36: https://github.com/giampaolo/psutil/issues/36
+.. _37: https://github.com/giampaolo/psutil/issues/37
+.. _38: https://github.com/giampaolo/psutil/issues/38
+.. _39: https://github.com/giampaolo/psutil/issues/39
+.. _40: https://github.com/giampaolo/psutil/issues/40
+.. _41: https://github.com/giampaolo/psutil/issues/41
+.. _42: https://github.com/giampaolo/psutil/issues/42
+.. _43: https://github.com/giampaolo/psutil/issues/43
+.. _44: https://github.com/giampaolo/psutil/issues/44
+.. _45: https://github.com/giampaolo/psutil/issues/45
+.. _46: https://github.com/giampaolo/psutil/issues/46
+.. _47: https://github.com/giampaolo/psutil/issues/47
+.. _48: https://github.com/giampaolo/psutil/issues/48
+.. _49: https://github.com/giampaolo/psutil/issues/49
+.. _50: https://github.com/giampaolo/psutil/issues/50
+.. _51: https://github.com/giampaolo/psutil/issues/51
+.. _52: https://github.com/giampaolo/psutil/issues/52
+.. _53: https://github.com/giampaolo/psutil/issues/53
+.. _54: https://github.com/giampaolo/psutil/issues/54
+.. _55: https://github.com/giampaolo/psutil/issues/55
+.. _56: https://github.com/giampaolo/psutil/issues/56
+.. _57: https://github.com/giampaolo/psutil/issues/57
+.. _58: https://github.com/giampaolo/psutil/issues/58
+.. _59: https://github.com/giampaolo/psutil/issues/59
+.. _60: https://github.com/giampaolo/psutil/issues/60
+.. _61: https://github.com/giampaolo/psutil/issues/61
+.. _62: https://github.com/giampaolo/psutil/issues/62
+.. _63: https://github.com/giampaolo/psutil/issues/63
+.. _64: https://github.com/giampaolo/psutil/issues/64
+.. _65: https://github.com/giampaolo/psutil/issues/65
+.. _66: https://github.com/giampaolo/psutil/issues/66
+.. _67: https://github.com/giampaolo/psutil/issues/67
+.. _68: https://github.com/giampaolo/psutil/issues/68
+.. _69: https://github.com/giampaolo/psutil/issues/69
+.. _70: https://github.com/giampaolo/psutil/issues/70
+.. _71: https://github.com/giampaolo/psutil/issues/71
+.. _72: https://github.com/giampaolo/psutil/issues/72
+.. _73: https://github.com/giampaolo/psutil/issues/73
+.. _74: https://github.com/giampaolo/psutil/issues/74
+.. _75: https://github.com/giampaolo/psutil/issues/75
+.. _76: https://github.com/giampaolo/psutil/issues/76
+.. _77: https://github.com/giampaolo/psutil/issues/77
+.. _78: https://github.com/giampaolo/psutil/issues/78
+.. _79: https://github.com/giampaolo/psutil/issues/79
+.. _80: https://github.com/giampaolo/psutil/issues/80
+.. _81: https://github.com/giampaolo/psutil/issues/81
+.. _82: https://github.com/giampaolo/psutil/issues/82
+.. _83: https://github.com/giampaolo/psutil/issues/83
+.. _84: https://github.com/giampaolo/psutil/issues/84
+.. _85: https://github.com/giampaolo/psutil/issues/85
+.. _86: https://github.com/giampaolo/psutil/issues/86
+.. _87: https://github.com/giampaolo/psutil/issues/87
+.. _88: https://github.com/giampaolo/psutil/issues/88
+.. _89: https://github.com/giampaolo/psutil/issues/89
+.. _90: https://github.com/giampaolo/psutil/issues/90
+.. _91: https://github.com/giampaolo/psutil/issues/91
+.. _92: https://github.com/giampaolo/psutil/issues/92
+.. _93: https://github.com/giampaolo/psutil/issues/93
+.. _94: https://github.com/giampaolo/psutil/issues/94
+.. _95: https://github.com/giampaolo/psutil/issues/95
+.. _96: https://github.com/giampaolo/psutil/issues/96
+.. _97: https://github.com/giampaolo/psutil/issues/97
+.. _98: https://github.com/giampaolo/psutil/issues/98
+.. _99: https://github.com/giampaolo/psutil/issues/99
+.. _100: https://github.com/giampaolo/psutil/issues/100
+.. _101: https://github.com/giampaolo/psutil/issues/101
+.. _102: https://github.com/giampaolo/psutil/issues/102
+.. _103: https://github.com/giampaolo/psutil/issues/103
+.. _104: https://github.com/giampaolo/psutil/issues/104
+.. _105: https://github.com/giampaolo/psutil/issues/105
+.. _106: https://github.com/giampaolo/psutil/issues/106
+.. _107: https://github.com/giampaolo/psutil/issues/107
+.. _108: https://github.com/giampaolo/psutil/issues/108
+.. _109: https://github.com/giampaolo/psutil/issues/109
+.. _110: https://github.com/giampaolo/psutil/issues/110
+.. _111: https://github.com/giampaolo/psutil/issues/111
+.. _112: https://github.com/giampaolo/psutil/issues/112
+.. _113: https://github.com/giampaolo/psutil/issues/113
+.. _114: https://github.com/giampaolo/psutil/issues/114
+.. _115: https://github.com/giampaolo/psutil/issues/115
+.. _116: https://github.com/giampaolo/psutil/issues/116
+.. _117: https://github.com/giampaolo/psutil/issues/117
+.. _118: https://github.com/giampaolo/psutil/issues/118
+.. _119: https://github.com/giampaolo/psutil/issues/119
+.. _120: https://github.com/giampaolo/psutil/issues/120
+.. _121: https://github.com/giampaolo/psutil/issues/121
+.. _122: https://github.com/giampaolo/psutil/issues/122
+.. _123: https://github.com/giampaolo/psutil/issues/123
+.. _124: https://github.com/giampaolo/psutil/issues/124
+.. _125: https://github.com/giampaolo/psutil/issues/125
+.. _126: https://github.com/giampaolo/psutil/issues/126
+.. _127: https://github.com/giampaolo/psutil/issues/127
+.. _128: https://github.com/giampaolo/psutil/issues/128
+.. _129: https://github.com/giampaolo/psutil/issues/129
+.. _130: https://github.com/giampaolo/psutil/issues/130
+.. _131: https://github.com/giampaolo/psutil/issues/131
+.. _132: https://github.com/giampaolo/psutil/issues/132
+.. _133: https://github.com/giampaolo/psutil/issues/133
+.. _134: https://github.com/giampaolo/psutil/issues/134
+.. _135: https://github.com/giampaolo/psutil/issues/135
+.. _136: https://github.com/giampaolo/psutil/issues/136
+.. _137: https://github.com/giampaolo/psutil/issues/137
+.. _138: https://github.com/giampaolo/psutil/issues/138
+.. _139: https://github.com/giampaolo/psutil/issues/139
+.. _140: https://github.com/giampaolo/psutil/issues/140
+.. _141: https://github.com/giampaolo/psutil/issues/141
+.. _142: https://github.com/giampaolo/psutil/issues/142
+.. _143: https://github.com/giampaolo/psutil/issues/143
+.. _144: https://github.com/giampaolo/psutil/issues/144
+.. _145: https://github.com/giampaolo/psutil/issues/145
+.. _146: https://github.com/giampaolo/psutil/issues/146
+.. _147: https://github.com/giampaolo/psutil/issues/147
+.. _148: https://github.com/giampaolo/psutil/issues/148
+.. _149: https://github.com/giampaolo/psutil/issues/149
+.. _150: https://github.com/giampaolo/psutil/issues/150
+.. _151: https://github.com/giampaolo/psutil/issues/151
+.. _152: https://github.com/giampaolo/psutil/issues/152
+.. _153: https://github.com/giampaolo/psutil/issues/153
+.. _154: https://github.com/giampaolo/psutil/issues/154
+.. _155: https://github.com/giampaolo/psutil/issues/155
+.. _156: https://github.com/giampaolo/psutil/issues/156
+.. _157: https://github.com/giampaolo/psutil/issues/157
+.. _158: https://github.com/giampaolo/psutil/issues/158
+.. _159: https://github.com/giampaolo/psutil/issues/159
+.. _160: https://github.com/giampaolo/psutil/issues/160
+.. _161: https://github.com/giampaolo/psutil/issues/161
+.. _162: https://github.com/giampaolo/psutil/issues/162
+.. _163: https://github.com/giampaolo/psutil/issues/163
+.. _164: https://github.com/giampaolo/psutil/issues/164
+.. _165: https://github.com/giampaolo/psutil/issues/165
+.. _166: https://github.com/giampaolo/psutil/issues/166
+.. _167: https://github.com/giampaolo/psutil/issues/167
+.. _168: https://github.com/giampaolo/psutil/issues/168
+.. _169: https://github.com/giampaolo/psutil/issues/169
+.. _170: https://github.com/giampaolo/psutil/issues/170
+.. _171: https://github.com/giampaolo/psutil/issues/171
+.. _172: https://github.com/giampaolo/psutil/issues/172
+.. _173: https://github.com/giampaolo/psutil/issues/173
+.. _174: https://github.com/giampaolo/psutil/issues/174
+.. _175: https://github.com/giampaolo/psutil/issues/175
+.. _176: https://github.com/giampaolo/psutil/issues/176
+.. _177: https://github.com/giampaolo/psutil/issues/177
+.. _178: https://github.com/giampaolo/psutil/issues/178
+.. _179: https://github.com/giampaolo/psutil/issues/179
+.. _180: https://github.com/giampaolo/psutil/issues/180
+.. _181: https://github.com/giampaolo/psutil/issues/181
+.. _182: https://github.com/giampaolo/psutil/issues/182
+.. _183: https://github.com/giampaolo/psutil/issues/183
+.. _184: https://github.com/giampaolo/psutil/issues/184
+.. _185: https://github.com/giampaolo/psutil/issues/185
+.. _186: https://github.com/giampaolo/psutil/issues/186
+.. _187: https://github.com/giampaolo/psutil/issues/187
+.. _188: https://github.com/giampaolo/psutil/issues/188
+.. _189: https://github.com/giampaolo/psutil/issues/189
+.. _190: https://github.com/giampaolo/psutil/issues/190
+.. _191: https://github.com/giampaolo/psutil/issues/191
+.. _192: https://github.com/giampaolo/psutil/issues/192
+.. _193: https://github.com/giampaolo/psutil/issues/193
+.. _194: https://github.com/giampaolo/psutil/issues/194
+.. _195: https://github.com/giampaolo/psutil/issues/195
+.. _196: https://github.com/giampaolo/psutil/issues/196
+.. _197: https://github.com/giampaolo/psutil/issues/197
+.. _198: https://github.com/giampaolo/psutil/issues/198
+.. _199: https://github.com/giampaolo/psutil/issues/199
+.. _200: https://github.com/giampaolo/psutil/issues/200
+.. _201: https://github.com/giampaolo/psutil/issues/201
+.. _202: https://github.com/giampaolo/psutil/issues/202
+.. _203: https://github.com/giampaolo/psutil/issues/203
+.. _204: https://github.com/giampaolo/psutil/issues/204
+.. _205: https://github.com/giampaolo/psutil/issues/205
+.. _206: https://github.com/giampaolo/psutil/issues/206
+.. _207: https://github.com/giampaolo/psutil/issues/207
+.. _208: https://github.com/giampaolo/psutil/issues/208
+.. _209: https://github.com/giampaolo/psutil/issues/209
+.. _210: https://github.com/giampaolo/psutil/issues/210
+.. _211: https://github.com/giampaolo/psutil/issues/211
+.. _212: https://github.com/giampaolo/psutil/issues/212
+.. _213: https://github.com/giampaolo/psutil/issues/213
+.. _214: https://github.com/giampaolo/psutil/issues/214
+.. _215: https://github.com/giampaolo/psutil/issues/215
+.. _216: https://github.com/giampaolo/psutil/issues/216
+.. _217: https://github.com/giampaolo/psutil/issues/217
+.. _218: https://github.com/giampaolo/psutil/issues/218
+.. _219: https://github.com/giampaolo/psutil/issues/219
+.. _220: https://github.com/giampaolo/psutil/issues/220
+.. _221: https://github.com/giampaolo/psutil/issues/221
+.. _222: https://github.com/giampaolo/psutil/issues/222
+.. _223: https://github.com/giampaolo/psutil/issues/223
+.. _224: https://github.com/giampaolo/psutil/issues/224
+.. _225: https://github.com/giampaolo/psutil/issues/225
+.. _226: https://github.com/giampaolo/psutil/issues/226
+.. _227: https://github.com/giampaolo/psutil/issues/227
+.. _228: https://github.com/giampaolo/psutil/issues/228
+.. _229: https://github.com/giampaolo/psutil/issues/229
+.. _230: https://github.com/giampaolo/psutil/issues/230
+.. _231: https://github.com/giampaolo/psutil/issues/231
+.. _232: https://github.com/giampaolo/psutil/issues/232
+.. _233: https://github.com/giampaolo/psutil/issues/233
+.. _234: https://github.com/giampaolo/psutil/issues/234
+.. _235: https://github.com/giampaolo/psutil/issues/235
+.. _236: https://github.com/giampaolo/psutil/issues/236
+.. _237: https://github.com/giampaolo/psutil/issues/237
+.. _238: https://github.com/giampaolo/psutil/issues/238
+.. _239: https://github.com/giampaolo/psutil/issues/239
+.. _240: https://github.com/giampaolo/psutil/issues/240
+.. _241: https://github.com/giampaolo/psutil/issues/241
+.. _242: https://github.com/giampaolo/psutil/issues/242
+.. _243: https://github.com/giampaolo/psutil/issues/243
+.. _244: https://github.com/giampaolo/psutil/issues/244
+.. _245: https://github.com/giampaolo/psutil/issues/245
+.. _246: https://github.com/giampaolo/psutil/issues/246
+.. _247: https://github.com/giampaolo/psutil/issues/247
+.. _248: https://github.com/giampaolo/psutil/issues/248
+.. _249: https://github.com/giampaolo/psutil/issues/249
+.. _250: https://github.com/giampaolo/psutil/issues/250
+.. _251: https://github.com/giampaolo/psutil/issues/251
+.. _252: https://github.com/giampaolo/psutil/issues/252
+.. _253: https://github.com/giampaolo/psutil/issues/253
+.. _254: https://github.com/giampaolo/psutil/issues/254
+.. _255: https://github.com/giampaolo/psutil/issues/255
+.. _256: https://github.com/giampaolo/psutil/issues/256
+.. _257: https://github.com/giampaolo/psutil/issues/257
+.. _258: https://github.com/giampaolo/psutil/issues/258
+.. _259: https://github.com/giampaolo/psutil/issues/259
+.. _260: https://github.com/giampaolo/psutil/issues/260
+.. _261: https://github.com/giampaolo/psutil/issues/261
+.. _262: https://github.com/giampaolo/psutil/issues/262
+.. _263: https://github.com/giampaolo/psutil/issues/263
+.. _264: https://github.com/giampaolo/psutil/issues/264
+.. _265: https://github.com/giampaolo/psutil/issues/265
+.. _266: https://github.com/giampaolo/psutil/issues/266
+.. _267: https://github.com/giampaolo/psutil/issues/267
+.. _268: https://github.com/giampaolo/psutil/issues/268
+.. _269: https://github.com/giampaolo/psutil/issues/269
+.. _270: https://github.com/giampaolo/psutil/issues/270
+.. _271: https://github.com/giampaolo/psutil/issues/271
+.. _272: https://github.com/giampaolo/psutil/issues/272
+.. _273: https://github.com/giampaolo/psutil/issues/273
+.. _274: https://github.com/giampaolo/psutil/issues/274
+.. _275: https://github.com/giampaolo/psutil/issues/275
+.. _276: https://github.com/giampaolo/psutil/issues/276
+.. _277: https://github.com/giampaolo/psutil/issues/277
+.. _278: https://github.com/giampaolo/psutil/issues/278
+.. _279: https://github.com/giampaolo/psutil/issues/279
+.. _280: https://github.com/giampaolo/psutil/issues/280
+.. _281: https://github.com/giampaolo/psutil/issues/281
+.. _282: https://github.com/giampaolo/psutil/issues/282
+.. _283: https://github.com/giampaolo/psutil/issues/283
+.. _284: https://github.com/giampaolo/psutil/issues/284
+.. _285: https://github.com/giampaolo/psutil/issues/285
+.. _286: https://github.com/giampaolo/psutil/issues/286
+.. _287: https://github.com/giampaolo/psutil/issues/287
+.. _288: https://github.com/giampaolo/psutil/issues/288
+.. _289: https://github.com/giampaolo/psutil/issues/289
+.. _290: https://github.com/giampaolo/psutil/issues/290
+.. _291: https://github.com/giampaolo/psutil/issues/291
+.. _292: https://github.com/giampaolo/psutil/issues/292
+.. _293: https://github.com/giampaolo/psutil/issues/293
+.. _294: https://github.com/giampaolo/psutil/issues/294
+.. _295: https://github.com/giampaolo/psutil/issues/295
+.. _296: https://github.com/giampaolo/psutil/issues/296
+.. _297: https://github.com/giampaolo/psutil/issues/297
+.. _298: https://github.com/giampaolo/psutil/issues/298
+.. _299: https://github.com/giampaolo/psutil/issues/299
+.. _300: https://github.com/giampaolo/psutil/issues/300
+.. _301: https://github.com/giampaolo/psutil/issues/301
+.. _302: https://github.com/giampaolo/psutil/issues/302
+.. _303: https://github.com/giampaolo/psutil/issues/303
+.. _304: https://github.com/giampaolo/psutil/issues/304
+.. _305: https://github.com/giampaolo/psutil/issues/305
+.. _306: https://github.com/giampaolo/psutil/issues/306
+.. _307: https://github.com/giampaolo/psutil/issues/307
+.. _308: https://github.com/giampaolo/psutil/issues/308
+.. _309: https://github.com/giampaolo/psutil/issues/309
+.. _310: https://github.com/giampaolo/psutil/issues/310
+.. _311: https://github.com/giampaolo/psutil/issues/311
+.. _312: https://github.com/giampaolo/psutil/issues/312
+.. _313: https://github.com/giampaolo/psutil/issues/313
+.. _314: https://github.com/giampaolo/psutil/issues/314
+.. _315: https://github.com/giampaolo/psutil/issues/315
+.. _316: https://github.com/giampaolo/psutil/issues/316
+.. _317: https://github.com/giampaolo/psutil/issues/317
+.. _318: https://github.com/giampaolo/psutil/issues/318
+.. _319: https://github.com/giampaolo/psutil/issues/319
+.. _320: https://github.com/giampaolo/psutil/issues/320
+.. _321: https://github.com/giampaolo/psutil/issues/321
+.. _322: https://github.com/giampaolo/psutil/issues/322
+.. _323: https://github.com/giampaolo/psutil/issues/323
+.. _324: https://github.com/giampaolo/psutil/issues/324
+.. _325: https://github.com/giampaolo/psutil/issues/325
+.. _326: https://github.com/giampaolo/psutil/issues/326
+.. _327: https://github.com/giampaolo/psutil/issues/327
+.. _328: https://github.com/giampaolo/psutil/issues/328
+.. _329: https://github.com/giampaolo/psutil/issues/329
+.. _330: https://github.com/giampaolo/psutil/issues/330
+.. _331: https://github.com/giampaolo/psutil/issues/331
+.. _332: https://github.com/giampaolo/psutil/issues/332
+.. _333: https://github.com/giampaolo/psutil/issues/333
+.. _334: https://github.com/giampaolo/psutil/issues/334
+.. _335: https://github.com/giampaolo/psutil/issues/335
+.. _336: https://github.com/giampaolo/psutil/issues/336
+.. _337: https://github.com/giampaolo/psutil/issues/337
+.. _338: https://github.com/giampaolo/psutil/issues/338
+.. _339: https://github.com/giampaolo/psutil/issues/339
+.. _340: https://github.com/giampaolo/psutil/issues/340
+.. _341: https://github.com/giampaolo/psutil/issues/341
+.. _342: https://github.com/giampaolo/psutil/issues/342
+.. _343: https://github.com/giampaolo/psutil/issues/343
+.. _344: https://github.com/giampaolo/psutil/issues/344
+.. _345: https://github.com/giampaolo/psutil/issues/345
+.. _346: https://github.com/giampaolo/psutil/issues/346
+.. _347: https://github.com/giampaolo/psutil/issues/347
+.. _348: https://github.com/giampaolo/psutil/issues/348
+.. _349: https://github.com/giampaolo/psutil/issues/349
+.. _350: https://github.com/giampaolo/psutil/issues/350
+.. _351: https://github.com/giampaolo/psutil/issues/351
+.. _352: https://github.com/giampaolo/psutil/issues/352
+.. _353: https://github.com/giampaolo/psutil/issues/353
+.. _354: https://github.com/giampaolo/psutil/issues/354
+.. _355: https://github.com/giampaolo/psutil/issues/355
+.. _356: https://github.com/giampaolo/psutil/issues/356
+.. _357: https://github.com/giampaolo/psutil/issues/357
+.. _358: https://github.com/giampaolo/psutil/issues/358
+.. _359: https://github.com/giampaolo/psutil/issues/359
+.. _360: https://github.com/giampaolo/psutil/issues/360
+.. _361: https://github.com/giampaolo/psutil/issues/361
+.. _362: https://github.com/giampaolo/psutil/issues/362
+.. _363: https://github.com/giampaolo/psutil/issues/363
+.. _364: https://github.com/giampaolo/psutil/issues/364
+.. _365: https://github.com/giampaolo/psutil/issues/365
+.. _366: https://github.com/giampaolo/psutil/issues/366
+.. _367: https://github.com/giampaolo/psutil/issues/367
+.. _368: https://github.com/giampaolo/psutil/issues/368
+.. _369: https://github.com/giampaolo/psutil/issues/369
+.. _370: https://github.com/giampaolo/psutil/issues/370
+.. _371: https://github.com/giampaolo/psutil/issues/371
+.. _372: https://github.com/giampaolo/psutil/issues/372
+.. _373: https://github.com/giampaolo/psutil/issues/373
+.. _374: https://github.com/giampaolo/psutil/issues/374
+.. _375: https://github.com/giampaolo/psutil/issues/375
+.. _376: https://github.com/giampaolo/psutil/issues/376
+.. _377: https://github.com/giampaolo/psutil/issues/377
+.. _378: https://github.com/giampaolo/psutil/issues/378
+.. _379: https://github.com/giampaolo/psutil/issues/379
+.. _380: https://github.com/giampaolo/psutil/issues/380
+.. _381: https://github.com/giampaolo/psutil/issues/381
+.. _382: https://github.com/giampaolo/psutil/issues/382
+.. _383: https://github.com/giampaolo/psutil/issues/383
+.. _384: https://github.com/giampaolo/psutil/issues/384
+.. _385: https://github.com/giampaolo/psutil/issues/385
+.. _386: https://github.com/giampaolo/psutil/issues/386
+.. _387: https://github.com/giampaolo/psutil/issues/387
+.. _388: https://github.com/giampaolo/psutil/issues/388
+.. _389: https://github.com/giampaolo/psutil/issues/389
+.. _390: https://github.com/giampaolo/psutil/issues/390
+.. _391: https://github.com/giampaolo/psutil/issues/391
+.. _392: https://github.com/giampaolo/psutil/issues/392
+.. _393: https://github.com/giampaolo/psutil/issues/393
+.. _394: https://github.com/giampaolo/psutil/issues/394
+.. _395: https://github.com/giampaolo/psutil/issues/395
+.. _396: https://github.com/giampaolo/psutil/issues/396
+.. _397: https://github.com/giampaolo/psutil/issues/397
+.. _398: https://github.com/giampaolo/psutil/issues/398
+.. _399: https://github.com/giampaolo/psutil/issues/399
+.. _400: https://github.com/giampaolo/psutil/issues/400
+.. _401: https://github.com/giampaolo/psutil/issues/401
+.. _402: https://github.com/giampaolo/psutil/issues/402
+.. _403: https://github.com/giampaolo/psutil/issues/403
+.. _404: https://github.com/giampaolo/psutil/issues/404
+.. _405: https://github.com/giampaolo/psutil/issues/405
+.. _406: https://github.com/giampaolo/psutil/issues/406
+.. _407: https://github.com/giampaolo/psutil/issues/407
+.. _408: https://github.com/giampaolo/psutil/issues/408
+.. _409: https://github.com/giampaolo/psutil/issues/409
+.. _410: https://github.com/giampaolo/psutil/issues/410
+.. _411: https://github.com/giampaolo/psutil/issues/411
+.. _412: https://github.com/giampaolo/psutil/issues/412
+.. _413: https://github.com/giampaolo/psutil/issues/413
+.. _414: https://github.com/giampaolo/psutil/issues/414
+.. _415: https://github.com/giampaolo/psutil/issues/415
+.. _416: https://github.com/giampaolo/psutil/issues/416
+.. _417: https://github.com/giampaolo/psutil/issues/417
+.. _418: https://github.com/giampaolo/psutil/issues/418
+.. _419: https://github.com/giampaolo/psutil/issues/419
+.. _420: https://github.com/giampaolo/psutil/issues/420
+.. _421: https://github.com/giampaolo/psutil/issues/421
+.. _422: https://github.com/giampaolo/psutil/issues/422
+.. _423: https://github.com/giampaolo/psutil/issues/423
+.. _424: https://github.com/giampaolo/psutil/issues/424
+.. _425: https://github.com/giampaolo/psutil/issues/425
+.. _426: https://github.com/giampaolo/psutil/issues/426
+.. _427: https://github.com/giampaolo/psutil/issues/427
+.. _428: https://github.com/giampaolo/psutil/issues/428
+.. _429: https://github.com/giampaolo/psutil/issues/429
+.. _430: https://github.com/giampaolo/psutil/issues/430
+.. _431: https://github.com/giampaolo/psutil/issues/431
+.. _432: https://github.com/giampaolo/psutil/issues/432
+.. _433: https://github.com/giampaolo/psutil/issues/433
+.. _434: https://github.com/giampaolo/psutil/issues/434
+.. _435: https://github.com/giampaolo/psutil/issues/435
+.. _436: https://github.com/giampaolo/psutil/issues/436
+.. _437: https://github.com/giampaolo/psutil/issues/437
+.. _438: https://github.com/giampaolo/psutil/issues/438
+.. _439: https://github.com/giampaolo/psutil/issues/439
+.. _440: https://github.com/giampaolo/psutil/issues/440
+.. _441: https://github.com/giampaolo/psutil/issues/441
+.. _442: https://github.com/giampaolo/psutil/issues/442
+.. _443: https://github.com/giampaolo/psutil/issues/443
+.. _444: https://github.com/giampaolo/psutil/issues/444
+.. _445: https://github.com/giampaolo/psutil/issues/445
+.. _446: https://github.com/giampaolo/psutil/issues/446
+.. _447: https://github.com/giampaolo/psutil/issues/447
+.. _448: https://github.com/giampaolo/psutil/issues/448
+.. _449: https://github.com/giampaolo/psutil/issues/449
+.. _450: https://github.com/giampaolo/psutil/issues/450
+.. _451: https://github.com/giampaolo/psutil/issues/451
+.. _452: https://github.com/giampaolo/psutil/issues/452
+.. _453: https://github.com/giampaolo/psutil/issues/453
+.. _454: https://github.com/giampaolo/psutil/issues/454
+.. _455: https://github.com/giampaolo/psutil/issues/455
+.. _456: https://github.com/giampaolo/psutil/issues/456
+.. _457: https://github.com/giampaolo/psutil/issues/457
+.. _458: https://github.com/giampaolo/psutil/issues/458
+.. _459: https://github.com/giampaolo/psutil/issues/459
+.. _460: https://github.com/giampaolo/psutil/issues/460
+.. _461: https://github.com/giampaolo/psutil/issues/461
+.. _462: https://github.com/giampaolo/psutil/issues/462
+.. _463: https://github.com/giampaolo/psutil/issues/463
+.. _464: https://github.com/giampaolo/psutil/issues/464
+.. _465: https://github.com/giampaolo/psutil/issues/465
+.. _466: https://github.com/giampaolo/psutil/issues/466
+.. _467: https://github.com/giampaolo/psutil/issues/467
+.. _468: https://github.com/giampaolo/psutil/issues/468
+.. _469: https://github.com/giampaolo/psutil/issues/469
+.. _470: https://github.com/giampaolo/psutil/issues/470
+.. _471: https://github.com/giampaolo/psutil/issues/471
+.. _472: https://github.com/giampaolo/psutil/issues/472
+.. _473: https://github.com/giampaolo/psutil/issues/473
+.. _474: https://github.com/giampaolo/psutil/issues/474
+.. _475: https://github.com/giampaolo/psutil/issues/475
+.. _476: https://github.com/giampaolo/psutil/issues/476
+.. _477: https://github.com/giampaolo/psutil/issues/477
+.. _478: https://github.com/giampaolo/psutil/issues/478
+.. _479: https://github.com/giampaolo/psutil/issues/479
+.. _480: https://github.com/giampaolo/psutil/issues/480
+.. _481: https://github.com/giampaolo/psutil/issues/481
+.. _482: https://github.com/giampaolo/psutil/issues/482
+.. _483: https://github.com/giampaolo/psutil/issues/483
+.. _484: https://github.com/giampaolo/psutil/issues/484
+.. _485: https://github.com/giampaolo/psutil/issues/485
+.. _486: https://github.com/giampaolo/psutil/issues/486
+.. _487: https://github.com/giampaolo/psutil/issues/487
+.. _488: https://github.com/giampaolo/psutil/issues/488
+.. _489: https://github.com/giampaolo/psutil/issues/489
+.. _490: https://github.com/giampaolo/psutil/issues/490
+.. _491: https://github.com/giampaolo/psutil/issues/491
+.. _492: https://github.com/giampaolo/psutil/issues/492
+.. _493: https://github.com/giampaolo/psutil/issues/493
+.. _494: https://github.com/giampaolo/psutil/issues/494
+.. _495: https://github.com/giampaolo/psutil/issues/495
+.. _496: https://github.com/giampaolo/psutil/issues/496
+.. _497: https://github.com/giampaolo/psutil/issues/497
+.. _498: https://github.com/giampaolo/psutil/issues/498
+.. _499: https://github.com/giampaolo/psutil/issues/499
+.. _500: https://github.com/giampaolo/psutil/issues/500
+.. _501: https://github.com/giampaolo/psutil/issues/501
+.. _502: https://github.com/giampaolo/psutil/issues/502
+.. _503: https://github.com/giampaolo/psutil/issues/503
+.. _504: https://github.com/giampaolo/psutil/issues/504
+.. _505: https://github.com/giampaolo/psutil/issues/505
+.. _506: https://github.com/giampaolo/psutil/issues/506
+.. _507: https://github.com/giampaolo/psutil/issues/507
+.. _508: https://github.com/giampaolo/psutil/issues/508
+.. _509: https://github.com/giampaolo/psutil/issues/509
+.. _510: https://github.com/giampaolo/psutil/issues/510
+.. _511: https://github.com/giampaolo/psutil/issues/511
+.. _512: https://github.com/giampaolo/psutil/issues/512
+.. _513: https://github.com/giampaolo/psutil/issues/513
+.. _514: https://github.com/giampaolo/psutil/issues/514
+.. _515: https://github.com/giampaolo/psutil/issues/515
+.. _516: https://github.com/giampaolo/psutil/issues/516
+.. _517: https://github.com/giampaolo/psutil/issues/517
+.. _518: https://github.com/giampaolo/psutil/issues/518
+.. _519: https://github.com/giampaolo/psutil/issues/519
+.. _520: https://github.com/giampaolo/psutil/issues/520
+.. _521: https://github.com/giampaolo/psutil/issues/521
+.. _522: https://github.com/giampaolo/psutil/issues/522
+.. _523: https://github.com/giampaolo/psutil/issues/523
+.. _524: https://github.com/giampaolo/psutil/issues/524
+.. _525: https://github.com/giampaolo/psutil/issues/525
+.. _526: https://github.com/giampaolo/psutil/issues/526
+.. _527: https://github.com/giampaolo/psutil/issues/527
+.. _528: https://github.com/giampaolo/psutil/issues/528
+.. _529: https://github.com/giampaolo/psutil/issues/529
+.. _530: https://github.com/giampaolo/psutil/issues/530
+.. _531: https://github.com/giampaolo/psutil/issues/531
+.. _532: https://github.com/giampaolo/psutil/issues/532
+.. _533: https://github.com/giampaolo/psutil/issues/533
+.. _534: https://github.com/giampaolo/psutil/issues/534
+.. _535: https://github.com/giampaolo/psutil/issues/535
+.. _536: https://github.com/giampaolo/psutil/issues/536
+.. _537: https://github.com/giampaolo/psutil/issues/537
+.. _538: https://github.com/giampaolo/psutil/issues/538
+.. _539: https://github.com/giampaolo/psutil/issues/539
+.. _540: https://github.com/giampaolo/psutil/issues/540
+.. _541: https://github.com/giampaolo/psutil/issues/541
+.. _542: https://github.com/giampaolo/psutil/issues/542
+.. _543: https://github.com/giampaolo/psutil/issues/543
+.. _544: https://github.com/giampaolo/psutil/issues/544
+.. _545: https://github.com/giampaolo/psutil/issues/545
+.. _546: https://github.com/giampaolo/psutil/issues/546
+.. _547: https://github.com/giampaolo/psutil/issues/547
+.. _548: https://github.com/giampaolo/psutil/issues/548
+.. _549: https://github.com/giampaolo/psutil/issues/549
+.. _550: https://github.com/giampaolo/psutil/issues/550
+.. _551: https://github.com/giampaolo/psutil/issues/551
+.. _552: https://github.com/giampaolo/psutil/issues/552
+.. _553: https://github.com/giampaolo/psutil/issues/553
+.. _554: https://github.com/giampaolo/psutil/issues/554
+.. _555: https://github.com/giampaolo/psutil/issues/555
+.. _556: https://github.com/giampaolo/psutil/issues/556
+.. _557: https://github.com/giampaolo/psutil/issues/557
+.. _558: https://github.com/giampaolo/psutil/issues/558
+.. _559: https://github.com/giampaolo/psutil/issues/559
+.. _560: https://github.com/giampaolo/psutil/issues/560
+.. _561: https://github.com/giampaolo/psutil/issues/561
+.. _562: https://github.com/giampaolo/psutil/issues/562
+.. _563: https://github.com/giampaolo/psutil/issues/563
+.. _564: https://github.com/giampaolo/psutil/issues/564
+.. _565: https://github.com/giampaolo/psutil/issues/565
+.. _566: https://github.com/giampaolo/psutil/issues/566
+.. _567: https://github.com/giampaolo/psutil/issues/567
+.. _568: https://github.com/giampaolo/psutil/issues/568
+.. _569: https://github.com/giampaolo/psutil/issues/569
+.. _570: https://github.com/giampaolo/psutil/issues/570
+.. _571: https://github.com/giampaolo/psutil/issues/571
+.. _572: https://github.com/giampaolo/psutil/issues/572
+.. _573: https://github.com/giampaolo/psutil/issues/573
+.. _574: https://github.com/giampaolo/psutil/issues/574
+.. _575: https://github.com/giampaolo/psutil/issues/575
+.. _576: https://github.com/giampaolo/psutil/issues/576
+.. _577: https://github.com/giampaolo/psutil/issues/577
+.. _578: https://github.com/giampaolo/psutil/issues/578
+.. _579: https://github.com/giampaolo/psutil/issues/579
+.. _580: https://github.com/giampaolo/psutil/issues/580
+.. _581: https://github.com/giampaolo/psutil/issues/581
+.. _582: https://github.com/giampaolo/psutil/issues/582
+.. _583: https://github.com/giampaolo/psutil/issues/583
+.. _584: https://github.com/giampaolo/psutil/issues/584
+.. _585: https://github.com/giampaolo/psutil/issues/585
+.. _586: https://github.com/giampaolo/psutil/issues/586
+.. _587: https://github.com/giampaolo/psutil/issues/587
+.. _588: https://github.com/giampaolo/psutil/issues/588
+.. _589: https://github.com/giampaolo/psutil/issues/589
+.. _590: https://github.com/giampaolo/psutil/issues/590
+.. _591: https://github.com/giampaolo/psutil/issues/591
+.. _592: https://github.com/giampaolo/psutil/issues/592
+.. _593: https://github.com/giampaolo/psutil/issues/593
+.. _594: https://github.com/giampaolo/psutil/issues/594
+.. _595: https://github.com/giampaolo/psutil/issues/595
+.. _596: https://github.com/giampaolo/psutil/issues/596
+.. _597: https://github.com/giampaolo/psutil/issues/597
+.. _598: https://github.com/giampaolo/psutil/issues/598
+.. _599: https://github.com/giampaolo/psutil/issues/599
+.. _600: https://github.com/giampaolo/psutil/issues/600
+.. _601: https://github.com/giampaolo/psutil/issues/601
+.. _602: https://github.com/giampaolo/psutil/issues/602
+.. _603: https://github.com/giampaolo/psutil/issues/603
+.. _604: https://github.com/giampaolo/psutil/issues/604
+.. _605: https://github.com/giampaolo/psutil/issues/605
+.. _606: https://github.com/giampaolo/psutil/issues/606
+.. _607: https://github.com/giampaolo/psutil/issues/607
+.. _608: https://github.com/giampaolo/psutil/issues/608
+.. _609: https://github.com/giampaolo/psutil/issues/609
+.. _610: https://github.com/giampaolo/psutil/issues/610
+.. _611: https://github.com/giampaolo/psutil/issues/611
+.. _612: https://github.com/giampaolo/psutil/issues/612
+.. _613: https://github.com/giampaolo/psutil/issues/613
+.. _614: https://github.com/giampaolo/psutil/issues/614
+.. _615: https://github.com/giampaolo/psutil/issues/615
+.. _616: https://github.com/giampaolo/psutil/issues/616
+.. _617: https://github.com/giampaolo/psutil/issues/617
+.. _618: https://github.com/giampaolo/psutil/issues/618
+.. _619: https://github.com/giampaolo/psutil/issues/619
+.. _620: https://github.com/giampaolo/psutil/issues/620
+.. _621: https://github.com/giampaolo/psutil/issues/621
+.. _622: https://github.com/giampaolo/psutil/issues/622
+.. _623: https://github.com/giampaolo/psutil/issues/623
+.. _624: https://github.com/giampaolo/psutil/issues/624
+.. _625: https://github.com/giampaolo/psutil/issues/625
+.. _626: https://github.com/giampaolo/psutil/issues/626
+.. _627: https://github.com/giampaolo/psutil/issues/627
+.. _628: https://github.com/giampaolo/psutil/issues/628
+.. _629: https://github.com/giampaolo/psutil/issues/629
+.. _630: https://github.com/giampaolo/psutil/issues/630
+.. _631: https://github.com/giampaolo/psutil/issues/631
+.. _632: https://github.com/giampaolo/psutil/issues/632
+.. _633: https://github.com/giampaolo/psutil/issues/633
+.. _634: https://github.com/giampaolo/psutil/issues/634
+.. _635: https://github.com/giampaolo/psutil/issues/635
+.. _636: https://github.com/giampaolo/psutil/issues/636
+.. _637: https://github.com/giampaolo/psutil/issues/637
+.. _638: https://github.com/giampaolo/psutil/issues/638
+.. _639: https://github.com/giampaolo/psutil/issues/639
+.. _640: https://github.com/giampaolo/psutil/issues/640
+.. _641: https://github.com/giampaolo/psutil/issues/641
+.. _642: https://github.com/giampaolo/psutil/issues/642
+.. _643: https://github.com/giampaolo/psutil/issues/643
+.. _644: https://github.com/giampaolo/psutil/issues/644
+.. _645: https://github.com/giampaolo/psutil/issues/645
+.. _646: https://github.com/giampaolo/psutil/issues/646
+.. _647: https://github.com/giampaolo/psutil/issues/647
+.. _648: https://github.com/giampaolo/psutil/issues/648
+.. _649: https://github.com/giampaolo/psutil/issues/649
+.. _650: https://github.com/giampaolo/psutil/issues/650
+.. _651: https://github.com/giampaolo/psutil/issues/651
+.. _652: https://github.com/giampaolo/psutil/issues/652
+.. _653: https://github.com/giampaolo/psutil/issues/653
+.. _654: https://github.com/giampaolo/psutil/issues/654
+.. _655: https://github.com/giampaolo/psutil/issues/655
+.. _656: https://github.com/giampaolo/psutil/issues/656
+.. _657: https://github.com/giampaolo/psutil/issues/657
+.. _658: https://github.com/giampaolo/psutil/issues/658
+.. _659: https://github.com/giampaolo/psutil/issues/659
+.. _660: https://github.com/giampaolo/psutil/issues/660
+.. _661: https://github.com/giampaolo/psutil/issues/661
+.. _662: https://github.com/giampaolo/psutil/issues/662
+.. _663: https://github.com/giampaolo/psutil/issues/663
+.. _664: https://github.com/giampaolo/psutil/issues/664
+.. _665: https://github.com/giampaolo/psutil/issues/665
+.. _666: https://github.com/giampaolo/psutil/issues/666
+.. _667: https://github.com/giampaolo/psutil/issues/667
+.. _668: https://github.com/giampaolo/psutil/issues/668
+.. _669: https://github.com/giampaolo/psutil/issues/669
+.. _670: https://github.com/giampaolo/psutil/issues/670
+.. _671: https://github.com/giampaolo/psutil/issues/671
+.. _672: https://github.com/giampaolo/psutil/issues/672
+.. _673: https://github.com/giampaolo/psutil/issues/673
+.. _674: https://github.com/giampaolo/psutil/issues/674
+.. _675: https://github.com/giampaolo/psutil/issues/675
+.. _676: https://github.com/giampaolo/psutil/issues/676
+.. _677: https://github.com/giampaolo/psutil/issues/677
+.. _678: https://github.com/giampaolo/psutil/issues/678
+.. _679: https://github.com/giampaolo/psutil/issues/679
+.. _680: https://github.com/giampaolo/psutil/issues/680
+.. _681: https://github.com/giampaolo/psutil/issues/681
+.. _682: https://github.com/giampaolo/psutil/issues/682
+.. _683: https://github.com/giampaolo/psutil/issues/683
+.. _684: https://github.com/giampaolo/psutil/issues/684
+.. _685: https://github.com/giampaolo/psutil/issues/685
+.. _686: https://github.com/giampaolo/psutil/issues/686
+.. _687: https://github.com/giampaolo/psutil/issues/687
+.. _688: https://github.com/giampaolo/psutil/issues/688
+.. _689: https://github.com/giampaolo/psutil/issues/689
+.. _690: https://github.com/giampaolo/psutil/issues/690
+.. _691: https://github.com/giampaolo/psutil/issues/691
+.. _692: https://github.com/giampaolo/psutil/issues/692
+.. _693: https://github.com/giampaolo/psutil/issues/693
+.. _694: https://github.com/giampaolo/psutil/issues/694
+.. _695: https://github.com/giampaolo/psutil/issues/695
+.. _696: https://github.com/giampaolo/psutil/issues/696
+.. _697: https://github.com/giampaolo/psutil/issues/697
+.. _698: https://github.com/giampaolo/psutil/issues/698
+.. _699: https://github.com/giampaolo/psutil/issues/699
+.. _700: https://github.com/giampaolo/psutil/issues/700
+.. _701: https://github.com/giampaolo/psutil/issues/701
+.. _702: https://github.com/giampaolo/psutil/issues/702
+.. _703: https://github.com/giampaolo/psutil/issues/703
+.. _704: https://github.com/giampaolo/psutil/issues/704
+.. _705: https://github.com/giampaolo/psutil/issues/705
+.. _706: https://github.com/giampaolo/psutil/issues/706
+.. _707: https://github.com/giampaolo/psutil/issues/707
+.. _708: https://github.com/giampaolo/psutil/issues/708
+.. _709: https://github.com/giampaolo/psutil/issues/709
+.. _710: https://github.com/giampaolo/psutil/issues/710
+.. _711: https://github.com/giampaolo/psutil/issues/711
+.. _712: https://github.com/giampaolo/psutil/issues/712
+.. _713: https://github.com/giampaolo/psutil/issues/713
+.. _714: https://github.com/giampaolo/psutil/issues/714
+.. _715: https://github.com/giampaolo/psutil/issues/715
+.. _716: https://github.com/giampaolo/psutil/issues/716
+.. _717: https://github.com/giampaolo/psutil/issues/717
+.. _718: https://github.com/giampaolo/psutil/issues/718
+.. _719: https://github.com/giampaolo/psutil/issues/719
+.. _720: https://github.com/giampaolo/psutil/issues/720
+.. _721: https://github.com/giampaolo/psutil/issues/721
+.. _722: https://github.com/giampaolo/psutil/issues/722
+.. _723: https://github.com/giampaolo/psutil/issues/723
+.. _724: https://github.com/giampaolo/psutil/issues/724
+.. _725: https://github.com/giampaolo/psutil/issues/725
+.. _726: https://github.com/giampaolo/psutil/issues/726
+.. _727: https://github.com/giampaolo/psutil/issues/727
+.. _728: https://github.com/giampaolo/psutil/issues/728
+.. _729: https://github.com/giampaolo/psutil/issues/729
+.. _730: https://github.com/giampaolo/psutil/issues/730
+.. _731: https://github.com/giampaolo/psutil/issues/731
+.. _732: https://github.com/giampaolo/psutil/issues/732
+.. _733: https://github.com/giampaolo/psutil/issues/733
+.. _734: https://github.com/giampaolo/psutil/issues/734
+.. _735: https://github.com/giampaolo/psutil/issues/735
+.. _736: https://github.com/giampaolo/psutil/issues/736
+.. _737: https://github.com/giampaolo/psutil/issues/737
+.. _738: https://github.com/giampaolo/psutil/issues/738
+.. _739: https://github.com/giampaolo/psutil/issues/739
+.. _740: https://github.com/giampaolo/psutil/issues/740
+.. _741: https://github.com/giampaolo/psutil/issues/741
+.. _742: https://github.com/giampaolo/psutil/issues/742
+.. _743: https://github.com/giampaolo/psutil/issues/743
+.. _744: https://github.com/giampaolo/psutil/issues/744
+.. _745: https://github.com/giampaolo/psutil/issues/745
+.. _746: https://github.com/giampaolo/psutil/issues/746
+.. _747: https://github.com/giampaolo/psutil/issues/747
+.. _748: https://github.com/giampaolo/psutil/issues/748
+.. _749: https://github.com/giampaolo/psutil/issues/749
+.. _750: https://github.com/giampaolo/psutil/issues/750
+.. _751: https://github.com/giampaolo/psutil/issues/751
+.. _752: https://github.com/giampaolo/psutil/issues/752
+.. _753: https://github.com/giampaolo/psutil/issues/753
+.. _754: https://github.com/giampaolo/psutil/issues/754
+.. _755: https://github.com/giampaolo/psutil/issues/755
+.. _756: https://github.com/giampaolo/psutil/issues/756
+.. _757: https://github.com/giampaolo/psutil/issues/757
+.. _758: https://github.com/giampaolo/psutil/issues/758
+.. _759: https://github.com/giampaolo/psutil/issues/759
+.. _760: https://github.com/giampaolo/psutil/issues/760
+.. _761: https://github.com/giampaolo/psutil/issues/761
+.. _762: https://github.com/giampaolo/psutil/issues/762
+.. _763: https://github.com/giampaolo/psutil/issues/763
+.. _764: https://github.com/giampaolo/psutil/issues/764
+.. _765: https://github.com/giampaolo/psutil/issues/765
+.. _766: https://github.com/giampaolo/psutil/issues/766
+.. _767: https://github.com/giampaolo/psutil/issues/767
+.. _768: https://github.com/giampaolo/psutil/issues/768
+.. _769: https://github.com/giampaolo/psutil/issues/769
+.. _770: https://github.com/giampaolo/psutil/issues/770
+.. _771: https://github.com/giampaolo/psutil/issues/771
+.. _772: https://github.com/giampaolo/psutil/issues/772
+.. _773: https://github.com/giampaolo/psutil/issues/773
+.. _774: https://github.com/giampaolo/psutil/issues/774
+.. _775: https://github.com/giampaolo/psutil/issues/775
+.. _776: https://github.com/giampaolo/psutil/issues/776
+.. _777: https://github.com/giampaolo/psutil/issues/777
+.. _778: https://github.com/giampaolo/psutil/issues/778
+.. _779: https://github.com/giampaolo/psutil/issues/779
+.. _780: https://github.com/giampaolo/psutil/issues/780
+.. _781: https://github.com/giampaolo/psutil/issues/781
+.. _782: https://github.com/giampaolo/psutil/issues/782
+.. _783: https://github.com/giampaolo/psutil/issues/783
+.. _784: https://github.com/giampaolo/psutil/issues/784
+.. _785: https://github.com/giampaolo/psutil/issues/785
+.. _786: https://github.com/giampaolo/psutil/issues/786
+.. _787: https://github.com/giampaolo/psutil/issues/787
+.. _788: https://github.com/giampaolo/psutil/issues/788
+.. _789: https://github.com/giampaolo/psutil/issues/789
+.. _790: https://github.com/giampaolo/psutil/issues/790
+.. _791: https://github.com/giampaolo/psutil/issues/791
+.. _792: https://github.com/giampaolo/psutil/issues/792
+.. _793: https://github.com/giampaolo/psutil/issues/793
+.. _794: https://github.com/giampaolo/psutil/issues/794
+.. _795: https://github.com/giampaolo/psutil/issues/795
+.. _796: https://github.com/giampaolo/psutil/issues/796
+.. _797: https://github.com/giampaolo/psutil/issues/797
+.. _798: https://github.com/giampaolo/psutil/issues/798
+.. _799: https://github.com/giampaolo/psutil/issues/799
+.. _800: https://github.com/giampaolo/psutil/issues/800
+.. _801: https://github.com/giampaolo/psutil/issues/801
+.. _802: https://github.com/giampaolo/psutil/issues/802
+.. _803: https://github.com/giampaolo/psutil/issues/803
+.. _804: https://github.com/giampaolo/psutil/issues/804
+.. _805: https://github.com/giampaolo/psutil/issues/805
+.. _806: https://github.com/giampaolo/psutil/issues/806
+.. _807: https://github.com/giampaolo/psutil/issues/807
+.. _808: https://github.com/giampaolo/psutil/issues/808
+.. _809: https://github.com/giampaolo/psutil/issues/809
+.. _810: https://github.com/giampaolo/psutil/issues/810
+.. _811: https://github.com/giampaolo/psutil/issues/811
+.. _812: https://github.com/giampaolo/psutil/issues/812
+.. _813: https://github.com/giampaolo/psutil/issues/813
+.. _814: https://github.com/giampaolo/psutil/issues/814
+.. _815: https://github.com/giampaolo/psutil/issues/815
+.. _816: https://github.com/giampaolo/psutil/issues/816
+.. _817: https://github.com/giampaolo/psutil/issues/817
+.. _818: https://github.com/giampaolo/psutil/issues/818
+.. _819: https://github.com/giampaolo/psutil/issues/819
+.. _820: https://github.com/giampaolo/psutil/issues/820
+.. _821: https://github.com/giampaolo/psutil/issues/821
+.. _822: https://github.com/giampaolo/psutil/issues/822
+.. _823: https://github.com/giampaolo/psutil/issues/823
+.. _824: https://github.com/giampaolo/psutil/issues/824
+.. _825: https://github.com/giampaolo/psutil/issues/825
+.. _826: https://github.com/giampaolo/psutil/issues/826
+.. _827: https://github.com/giampaolo/psutil/issues/827
+.. _828: https://github.com/giampaolo/psutil/issues/828
+.. _829: https://github.com/giampaolo/psutil/issues/829
+.. _830: https://github.com/giampaolo/psutil/issues/830
+.. _831: https://github.com/giampaolo/psutil/issues/831
+.. _832: https://github.com/giampaolo/psutil/issues/832
+.. _833: https://github.com/giampaolo/psutil/issues/833
+.. _834: https://github.com/giampaolo/psutil/issues/834
+.. _835: https://github.com/giampaolo/psutil/issues/835
+.. _836: https://github.com/giampaolo/psutil/issues/836
+.. _837: https://github.com/giampaolo/psutil/issues/837
+.. _838: https://github.com/giampaolo/psutil/issues/838
+.. _839: https://github.com/giampaolo/psutil/issues/839
+.. _840: https://github.com/giampaolo/psutil/issues/840
+.. _841: https://github.com/giampaolo/psutil/issues/841
+.. _842: https://github.com/giampaolo/psutil/issues/842
+.. _843: https://github.com/giampaolo/psutil/issues/843
+.. _844: https://github.com/giampaolo/psutil/issues/844
+.. _845: https://github.com/giampaolo/psutil/issues/845
+.. _846: https://github.com/giampaolo/psutil/issues/846
+.. _847: https://github.com/giampaolo/psutil/issues/847
+.. _848: https://github.com/giampaolo/psutil/issues/848
+.. _849: https://github.com/giampaolo/psutil/issues/849
+.. _850: https://github.com/giampaolo/psutil/issues/850
+.. _851: https://github.com/giampaolo/psutil/issues/851
+.. _852: https://github.com/giampaolo/psutil/issues/852
+.. _853: https://github.com/giampaolo/psutil/issues/853
+.. _854: https://github.com/giampaolo/psutil/issues/854
+.. _855: https://github.com/giampaolo/psutil/issues/855
+.. _856: https://github.com/giampaolo/psutil/issues/856
+.. _857: https://github.com/giampaolo/psutil/issues/857
+.. _858: https://github.com/giampaolo/psutil/issues/858
+.. _859: https://github.com/giampaolo/psutil/issues/859
+.. _860: https://github.com/giampaolo/psutil/issues/860
+.. _861: https://github.com/giampaolo/psutil/issues/861
+.. _862: https://github.com/giampaolo/psutil/issues/862
+.. _863: https://github.com/giampaolo/psutil/issues/863
+.. _864: https://github.com/giampaolo/psutil/issues/864
+.. _865: https://github.com/giampaolo/psutil/issues/865
+.. _866: https://github.com/giampaolo/psutil/issues/866
+.. _867: https://github.com/giampaolo/psutil/issues/867
+.. _868: https://github.com/giampaolo/psutil/issues/868
+.. _869: https://github.com/giampaolo/psutil/issues/869
+.. _870: https://github.com/giampaolo/psutil/issues/870
+.. _871: https://github.com/giampaolo/psutil/issues/871
+.. _872: https://github.com/giampaolo/psutil/issues/872
+.. _873: https://github.com/giampaolo/psutil/issues/873
+.. _874: https://github.com/giampaolo/psutil/issues/874
+.. _875: https://github.com/giampaolo/psutil/issues/875
+.. _876: https://github.com/giampaolo/psutil/issues/876
+.. _877: https://github.com/giampaolo/psutil/issues/877
+.. _878: https://github.com/giampaolo/psutil/issues/878
+.. _879: https://github.com/giampaolo/psutil/issues/879
+.. _880: https://github.com/giampaolo/psutil/issues/880
+.. _881: https://github.com/giampaolo/psutil/issues/881
+.. _882: https://github.com/giampaolo/psutil/issues/882
+.. _883: https://github.com/giampaolo/psutil/issues/883
+.. _884: https://github.com/giampaolo/psutil/issues/884
+.. _885: https://github.com/giampaolo/psutil/issues/885
+.. _886: https://github.com/giampaolo/psutil/issues/886
+.. _887: https://github.com/giampaolo/psutil/issues/887
+.. _888: https://github.com/giampaolo/psutil/issues/888
+.. _889: https://github.com/giampaolo/psutil/issues/889
+.. _890: https://github.com/giampaolo/psutil/issues/890
+.. _891: https://github.com/giampaolo/psutil/issues/891
+.. _892: https://github.com/giampaolo/psutil/issues/892
+.. _893: https://github.com/giampaolo/psutil/issues/893
+.. _894: https://github.com/giampaolo/psutil/issues/894
+.. _895: https://github.com/giampaolo/psutil/issues/895
+.. _896: https://github.com/giampaolo/psutil/issues/896
+.. _897: https://github.com/giampaolo/psutil/issues/897
+.. _898: https://github.com/giampaolo/psutil/issues/898
+.. _899: https://github.com/giampaolo/psutil/issues/899
+.. _900: https://github.com/giampaolo/psutil/issues/900
+.. _901: https://github.com/giampaolo/psutil/issues/901
+.. _902: https://github.com/giampaolo/psutil/issues/902
+.. _903: https://github.com/giampaolo/psutil/issues/903
+.. _904: https://github.com/giampaolo/psutil/issues/904
+.. _905: https://github.com/giampaolo/psutil/issues/905
+.. _906: https://github.com/giampaolo/psutil/issues/906
+.. _907: https://github.com/giampaolo/psutil/issues/907
+.. _908: https://github.com/giampaolo/psutil/issues/908
+.. _909: https://github.com/giampaolo/psutil/issues/909
+.. _910: https://github.com/giampaolo/psutil/issues/910
+.. _911: https://github.com/giampaolo/psutil/issues/911
+.. _912: https://github.com/giampaolo/psutil/issues/912
+.. _913: https://github.com/giampaolo/psutil/issues/913
+.. _914: https://github.com/giampaolo/psutil/issues/914
+.. _915: https://github.com/giampaolo/psutil/issues/915
+.. _916: https://github.com/giampaolo/psutil/issues/916
+.. _917: https://github.com/giampaolo/psutil/issues/917
+.. _918: https://github.com/giampaolo/psutil/issues/918
+.. _919: https://github.com/giampaolo/psutil/issues/919
+.. _920: https://github.com/giampaolo/psutil/issues/920
+.. _921: https://github.com/giampaolo/psutil/issues/921
+.. _922: https://github.com/giampaolo/psutil/issues/922
+.. _923: https://github.com/giampaolo/psutil/issues/923
+.. _924: https://github.com/giampaolo/psutil/issues/924
+.. _925: https://github.com/giampaolo/psutil/issues/925
+.. _926: https://github.com/giampaolo/psutil/issues/926
+.. _927: https://github.com/giampaolo/psutil/issues/927
+.. _928: https://github.com/giampaolo/psutil/issues/928
+.. _929: https://github.com/giampaolo/psutil/issues/929
+.. _930: https://github.com/giampaolo/psutil/issues/930
+.. _931: https://github.com/giampaolo/psutil/issues/931
+.. _932: https://github.com/giampaolo/psutil/issues/932
+.. _933: https://github.com/giampaolo/psutil/issues/933
+.. _934: https://github.com/giampaolo/psutil/issues/934
+.. _935: https://github.com/giampaolo/psutil/issues/935
+.. _936: https://github.com/giampaolo/psutil/issues/936
+.. _937: https://github.com/giampaolo/psutil/issues/937
+.. _938: https://github.com/giampaolo/psutil/issues/938
+.. _939: https://github.com/giampaolo/psutil/issues/939
+.. _940: https://github.com/giampaolo/psutil/issues/940
+.. _941: https://github.com/giampaolo/psutil/issues/941
+.. _942: https://github.com/giampaolo/psutil/issues/942
+.. _943: https://github.com/giampaolo/psutil/issues/943
+.. _944: https://github.com/giampaolo/psutil/issues/944
+.. _945: https://github.com/giampaolo/psutil/issues/945
+.. _946: https://github.com/giampaolo/psutil/issues/946
+.. _947: https://github.com/giampaolo/psutil/issues/947
+.. _948: https://github.com/giampaolo/psutil/issues/948
+.. _949: https://github.com/giampaolo/psutil/issues/949
+.. _950: https://github.com/giampaolo/psutil/issues/950
+.. _951: https://github.com/giampaolo/psutil/issues/951
+.. _952: https://github.com/giampaolo/psutil/issues/952
+.. _953: https://github.com/giampaolo/psutil/issues/953
+.. _954: https://github.com/giampaolo/psutil/issues/954
+.. _955: https://github.com/giampaolo/psutil/issues/955
+.. _956: https://github.com/giampaolo/psutil/issues/956
+.. _957: https://github.com/giampaolo/psutil/issues/957
+.. _958: https://github.com/giampaolo/psutil/issues/958
+.. _959: https://github.com/giampaolo/psutil/issues/959
+.. _960: https://github.com/giampaolo/psutil/issues/960
+.. _961: https://github.com/giampaolo/psutil/issues/961
+.. _962: https://github.com/giampaolo/psutil/issues/962
+.. _963: https://github.com/giampaolo/psutil/issues/963
+.. _964: https://github.com/giampaolo/psutil/issues/964
+.. _965: https://github.com/giampaolo/psutil/issues/965
+.. _966: https://github.com/giampaolo/psutil/issues/966
+.. _967: https://github.com/giampaolo/psutil/issues/967
+.. _968: https://github.com/giampaolo/psutil/issues/968
+.. _969: https://github.com/giampaolo/psutil/issues/969
+.. _970: https://github.com/giampaolo/psutil/issues/970
+.. _971: https://github.com/giampaolo/psutil/issues/971
+.. _972: https://github.com/giampaolo/psutil/issues/972
+.. _973: https://github.com/giampaolo/psutil/issues/973
+.. _974: https://github.com/giampaolo/psutil/issues/974
+.. _975: https://github.com/giampaolo/psutil/issues/975
+.. _976: https://github.com/giampaolo/psutil/issues/976
+.. _977: https://github.com/giampaolo/psutil/issues/977
+.. _978: https://github.com/giampaolo/psutil/issues/978
+.. _979: https://github.com/giampaolo/psutil/issues/979
+.. _980: https://github.com/giampaolo/psutil/issues/980
+.. _981: https://github.com/giampaolo/psutil/issues/981
+.. _982: https://github.com/giampaolo/psutil/issues/982
+.. _983: https://github.com/giampaolo/psutil/issues/983
+.. _984: https://github.com/giampaolo/psutil/issues/984
+.. _985: https://github.com/giampaolo/psutil/issues/985
+.. _986: https://github.com/giampaolo/psutil/issues/986
+.. _987: https://github.com/giampaolo/psutil/issues/987
+.. _988: https://github.com/giampaolo/psutil/issues/988
+.. _989: https://github.com/giampaolo/psutil/issues/989
+.. _990: https://github.com/giampaolo/psutil/issues/990
+.. _991: https://github.com/giampaolo/psutil/issues/991
+.. _992: https://github.com/giampaolo/psutil/issues/992
+.. _993: https://github.com/giampaolo/psutil/issues/993
+.. _994: https://github.com/giampaolo/psutil/issues/994
+.. _995: https://github.com/giampaolo/psutil/issues/995
+.. _996: https://github.com/giampaolo/psutil/issues/996
+.. _997: https://github.com/giampaolo/psutil/issues/997
+.. _998: https://github.com/giampaolo/psutil/issues/998
+.. _999: https://github.com/giampaolo/psutil/issues/999
diff --git a/IDEAS b/IDEAS
index 9eb7d762..a80c9dc0 100644
--- a/IDEAS
+++ b/IDEAS
@@ -17,10 +17,19 @@ PLATFORMS
FEATURES
========
-- (Linux): from /proc/pid/stat we can also retrieve process and children guest
- times (time spent running a virtual CPU for a guest OS).
+- #772: extended net_io_counters() metrics.
-- #809: (Linux) per-process resource limits.
+- #900: wheels for OSX and Linux.
+
+- #922: extended net_io_stats() info.
+
+- #914: extended platform specific process info.
+
+- #898: wifi stats
+
+- #893: (BSD) process environ
+
+- #809: (BSD) per-process resource limits (rlimit()).
- (UNIX) process root (different from cwd)
@@ -54,7 +63,6 @@ FEATURES
- system-wide number of open file descriptors:
- https://jira.hyperic.com/browse/SIGAR-30
- - http://www.netadmintools.com/part295.html
- Number of system threads.
- Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684824(v=vs.85).aspx
diff --git a/INSTALL.rst b/INSTALL.rst
index 05bbc9c3..b1b40d68 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -1,14 +1,24 @@
-*Note: pip is the easiest way to install psutil.
+PIP
+===
+
+pip is the easiest way to install psutil.
It is shipped by default with Python 2.7.9+ and 3.4+. If you're using an
-older Python version* `install pip <https://pip.pypa.io/en/latest/installing/>`__
-*first.* If you cloned psutil source code you can also install it with
-``make install-pip``.
+older Python version `install pip <https://pip.pypa.io/en/latest/installing/>`__
+first.
+If you GIT cloned psutil source code you can also install pip with::
+
+ make install-pip
-Permission issues
-=================
+Unless you're on Windows, in order to install psutil with pip you'll also need
+a C compiler installed.
+pip will retrieve psutil source code or binaries from
+`PYPI <https://pypi.python.org/pypi/psutil>`__ repository.
-Except for Linux, the commands below assume you're running as root.
-If you're not and you bump into permission errors you can either:
+Permission issues (UNIX)
+========================
+
+The commands below assume you're running as root.
+If you're not or you bump into permission errors you can either:
* prepend ``sudo``, e.g.:
@@ -25,16 +35,30 @@ If you're not and you bump into permission errors you can either:
Linux
=====
-Ubuntu / Debian (use ``python3-dev`` and ``python3-pip`` for python 3)::
+Ubuntu / Debian::
sudo apt-get install gcc python-dev python-pip
pip install psutil
-RedHat (use ``python3-devel`` and ``python3-pip`` for python 3)::
+RedHat / CentOS::
sudo yum install gcc python-devel python-pip
pip install psutil
+If you're on Python 3 use ``python3-dev`` and ``python3-pip`` instead.
+
+Major Linux distros also provide binary distributions of psutil so, for
+instance, on Ubuntu and Debian you can also do::
+
+ sudo apt-get install python-psutil
+
+On RedHat and CentOS::
+
+ sudo yum install python-psutil
+
+This is not recommended though as Linux distros usually ship older psutil
+versions.
+
OSX
===
@@ -49,7 +73,7 @@ Windows
=======
The easiest way to install psutil on Windows is to just use the pre-compiled
-exe/wheel installers on
+exe/wheel installers hosted on
`PYPI <https://pypi.python.org/pypi/psutil/#downloads>`__ via pip::
C:\Python27\python.exe -m pip install psutil
@@ -83,7 +107,7 @@ OpenBSD
::
- export PKG_PATH=http://ftp.usa.openbsd.org/pub/OpenBSD/`uname -r`/packages/`arch -s`
+ export PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/`uname -r`/packages/`arch -s`/"
pkg_add -v python gcc
python -m pip install psutil
diff --git a/MANIFEST.in b/MANIFEST.in
index e740309c..62df08ef 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -20,5 +20,4 @@ recursive-include .ci *
recursive-include docs *
recursive-include psutil *.py *.c *.h README*
recursive-include scripts *.py
-recursive-include scripts/internal *.py
-recursive-include scripts/internal/README*
+recursive-include scripts/internal *.py README*
diff --git a/Makefile b/Makefile
index 8886731d..d5fdc8cb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,38 @@
# Shortcuts for various tasks (UNIX only).
# To use a specific Python version run: "make install PYTHON=python3.3"
+# You can set the variables below from the command line.
-# You can set these variables from the command line.
PYTHON = python
TSCRIPT = psutil/tests/runner.py
+ARGS =
-# For internal use.
-DEPS = coverage \
+# List of nice-to-have dev libs.
+DEPS = argparse \
+ coverage \
flake8 \
futures \
- ipdb \
+ ipaddress \
mock==1.0.1 \
- nose \
pep8 \
pyflakes \
requests \
+ setuptools \
sphinx \
sphinx-pypi-upload \
+ twine \
unittest2
+# In not in a virtualenv, add --user options for install commands.
+INSTALL_OPTS = `$(PYTHON) -c "import sys; print('' if hasattr(sys, 'real_prefix') else '--user')"`
+
+
all: test
# ===================================================================
# Install
# ===================================================================
+# Remove all build files.
clean:
rm -f `find . -type f -name \*.py[co]`
rm -f `find . -type f -name \*.so`
@@ -44,45 +52,59 @@ clean:
rm -rf htmlcov/
rm -rf tmp/
-build: clean
+_:
+
+
+# Compile without installing.
+build: _
$(PYTHON) setup.py build
- @# copies *.so files in ./psutil directory in order to allow
+ @# copies compiled *.so files in ./psutil directory in order to allow
@# "import psutil" when using the interactive interpreter from within
@# this directory.
$(PYTHON) setup.py build_ext -i
rm -rf tmp
+# Install this package + GIT hooks. Install is done:
+# - as the current user, in order to avoid permission issues
+# - in development / edit mode, so that source can be modified on the fly
install: build
# make sure setuptools is installed (needed for 'develop' / edit mode)
$(PYTHON) -c "import setuptools"
- $(PYTHON) setup.py develop --user
+ $(PYTHON) setup.py develop $(INSTALL_OPTS)
rm -rf tmp
+# Uninstall this package via pip.
uninstall:
cd ..; $(PYTHON) -m pip uninstall -y -v psutil
+# Install PIP (only if necessary).
install-pip:
- # Install PIP (only if necessary).
- $(PYTHON) -c "import sys, ssl, os, pkgutil, tempfile, atexit; \
- sys.exit(0) if pkgutil.find_loader('pip') else None; \
- pyexc = 'from urllib.request import urlopen' if sys.version_info[0] == 3 else 'from urllib2 import urlopen'; \
- exec(pyexc); \
- context = ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None; \
- kw = dict(context=context) if context else {}; \
- req = urlopen('https://bootstrap.pypa.io/get-pip.py', **kw); \
- data = req.read(); \
- f = tempfile.NamedTemporaryFile(suffix='.py'); \
- atexit.register(f.close); \
- f.write(data); \
- f.flush(); \
- print('downloaded %s' % f.name); \
- code = os.system('%s %s --user' % (sys.executable, f.name)); \
- sys.exit(code);"
-
-# Install useful deps which are nice to have while developing / testing.
+ $(PYTHON) -c \
+ "import sys, ssl, os, pkgutil, tempfile, atexit; \
+ sys.exit(0) if pkgutil.find_loader('pip') else None; \
+ pyexc = 'from urllib.request import urlopen' if sys.version_info[0] == 3 else 'from urllib2 import urlopen'; \
+ exec(pyexc); \
+ ctx = ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None; \
+ kw = dict(context=ctx) if ctx else {}; \
+ req = urlopen('https://bootstrap.pypa.io/get-pip.py', **kw); \
+ data = req.read(); \
+ f = tempfile.NamedTemporaryFile(suffix='.py'); \
+ atexit.register(f.close); \
+ f.write(data); \
+ f.flush(); \
+ print('downloaded %s' % f.name); \
+ code = os.system('%s %s --user' % (sys.executable, f.name)); \
+ f.close(); \
+ sys.exit(code);"
+
+# Install:
+# - GIT hooks
+# - pip (if necessary)
+# - useful deps which are nice to have while developing / testing;
+# deps these are also upgraded
setup-dev-env: install-git-hooks install-pip
- $(PYTHON) -m pip install --user --upgrade pip
- $(PYTHON) -m pip install --user --upgrade $(DEPS)
+ $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade pip
+ $(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade $(DEPS)
# ===================================================================
# Tests
@@ -104,6 +126,10 @@ test-system: install
test-misc: install
$(PYTHON) psutil/tests/test_misc.py
+# Test POSIX.
+test-posix: install
+ $(PYTHON) psutil/tests/test_posix.py
+
# Test memory leaks.
test-memleaks: install
$(PYTHON) psutil/tests/test_memory_leaks.py
@@ -112,15 +138,10 @@ test-memleaks: install
test-platform: install
$(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS") if getattr(psutil, x)][0])'`.py
-# Run a specific test by name; e.g. "make test-by-name disk_" will run
-# all test methods containing "disk_" in their name.
-# Requires "pip install nose".
+# Run a specific test by name, e.g.
+# make test-by-name psutil.tests.test_system.TestSystemAPIs.test_cpu_times
test-by-name: install
- @$(PYTHON) -m nose psutil/tests/*.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
-
-# Same as above but for test_memory_leaks.py script.
-test-memleaks-by-name: install
- @$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
+ @$(PYTHON) -m unittest -v $(ARGS)
coverage: install
# Note: coverage options are controlled by .coveragerc file
@@ -154,7 +175,7 @@ git-tag-release:
git tag -a release-`python -c "import setup; print(setup.get_version())"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD`
git push --follow-tags
-# install GIT pre-commit hook
+# Install GIT pre-commit hook.
install-git-hooks:
ln -sf ../../.git-pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
@@ -170,21 +191,59 @@ upload-src: clean
# Build and upload doc on https://pythonhosted.org/psutil/.
# Requires "pip install sphinx-pypi-upload".
upload-doc:
- cd docs; make html
+ cd docs && make html
$(PYTHON) setup.py upload_sphinx --upload-dir=docs/_build/html
-# download exes/wheels hosted on appveyor
+# Download exes/wheels hosted on appveyor.
win-download-exes:
- $(PYTHON) .ci/appveyor/download_exes.py --user giampaolo --project psutil
+ $(PYTHON) scripts/internal/download_exes.py --user giampaolo --project psutil
-# upload exes/wheels in dist/* directory to PYPI
+# Upload exes/wheels in dist/* directory to PYPI.
win-upload-exes:
- $(PYTHON) -m twine upload dist/*
+ $(PYTHON) -m twine upload dist/*.exe
+ $(PYTHON) -m twine upload dist/*.wheel
+
+# All the necessary steps before making a release.
+pre-release:
+ ${MAKE} clean
+ ${MAKE} install # to import psutil from download_exes.py
+ $(PYTHON) -c \
+ "from psutil import __version__ as ver; \
+ doc = open('docs/index.rst').read(); \
+ history = open('HISTORY.rst').read(); \
+ assert ver in doc, '%r not in docs/index.rst' % ver; \
+ assert ver in history, '%r not in HISTORY.rst' % ver; \
+ assert 'XXXX' not in history; \
+ "
+ ${MAKE} setup-dev-env # mainly to update sphinx and install twine
+ ${MAKE} win-download-exes
+ $(PYTHON) setup.py sdist
+
+# Create a release: creates tar.gz and exes/wheels, uploads them,
+# upload doc, git tag release.
+release:
+ ${MAKE} pre-release
+ $(PYTHON) -m twine upload dist/* # upload tar.gz, exes, wheels on PYPI
+ ${MAKE} git-tag-release
+
+# Print announce of new release.
+print-announce:
+ @$(PYTHON) scripts/internal/print_announce.py
# ===================================================================
-# Others
+# Misc
# ===================================================================
+grep-todos:
+ git grep -EIn "TODO|FIXME|XXX"
+
# run script which benchmarks oneshot() ctx manager (see #799)
bench-oneshot: install
$(PYTHON) scripts/internal/bench_oneshot.py
+
+# same as above but using perf module (supposed to be more precise)
+bench-oneshot-2: install
+ rm -f normal.json oneshot.json
+ $(PYTHON) scripts/internal/bench_oneshot_2.py normal -o normal.json
+ $(PYTHON) scripts/internal/bench_oneshot_2.py oneshot -o oneshot.json
+ $(PYTHON) -m perf compare_to normal.json oneshot.json
diff --git a/README.rst b/README.rst
index eed24832..0a6ec01a 100644
--- a/README.rst
+++ b/README.rst
@@ -61,6 +61,14 @@ Example applications
- https://github.com/Jahaja/psdash
- https://github.com/giampaolo/psutil/tree/master/scripts
++------------------------------------------------+---------------------------------------------+
+| .. image:: docs/_static/procinfo-small.png | .. image:: docs/_static/top-small.png |
+| :target: docs/_static/procinfo.png | :target: docs/_static/top.png |
++------------------------------------------------+---------------------------------------------+
+| .. image:: docs/_static/procsmem-small.png | .. image:: docs/_static/pmap-small.png |
+| :target: docs/_static/procsmem.png | :target: docs/_static/pmap.png |
++------------------------------------------------+---------------------------------------------+
+
==============
Example usages
==============
@@ -349,59 +357,10 @@ I only ask for a small donation, but of course I appreciate any amount.
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
:alt: Donate via PayPal
-Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin <http://www.linkedin.com/in/grodola>`_.
+Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin <https://www.linkedin.com/in/grodola>`_.
============
Mailing list
============
http://groups.google.com/group/psutil/
-
-========
-Timeline
-========
-
-- 2016-06-18: `psutil-4.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-4.3.0.tar.gz>`_
-- 2016-05-15: `psutil-4.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-4.2.0.tar.gz>`_
-- 2016-03-12: `psutil-4.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-4.1.0.tar.gz>`_
-- 2016-02-17: `psutil-4.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-4.0.0.tar.gz>`_
-- 2016-01-20: `psutil-3.4.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.4.2.tar.gz>`_
-- 2016-01-15: `psutil-3.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.4.1.tar.gz>`_
-- 2015-11-25: `psutil-3.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.3.0.tar.gz>`_
-- 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>`_
-- 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
-- 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
-- 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
-- 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
-- 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
-- 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
-- 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
-- 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
-- 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
-- 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
-- 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
-- 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
-- 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
-- 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.2.tar.gz>`_
-- 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.1.tar.gz>`_
-- 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.0.tar.gz>`_
-- 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.1.tar.gz>`_
-- 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.0.tar.gz>`_
-- 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.1.tar.gz>`_
-- 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.0.tar.gz>`_
-- 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.1.tar.gz>`_
-- 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.0.tar.gz>`_
-- 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.1.tar.gz>`_
-- 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.0.tar.gz>`_
-- 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.1.tar.gz>`_
-- 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.0.tar.gz>`_
-- 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
-- 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
-- 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
-- 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
-- 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
-- 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
-- 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
diff --git a/appveyor.yml b/appveyor.yml
index 52997204..801df0f3 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -91,3 +91,25 @@ artifacts:
skip_commits:
message: skip-ci
+
+# run build only if one of the following files is modified on commit
+only_commits:
+ files:
+ .ci/appveyor/*
+ appveyor.yml
+ psutil/__init__.py
+ psutil/_common.py
+ psutil/_compat.py
+ psutil/_psutil_common.*
+ psutil/_psutil_windows.*
+ psutil/_pswindows.py
+ psutil/arch/windows/*
+ psutil/tests/__init__.py
+ psutil/tests/runner.py
+ psutil/tests/test_memory_leaks.py
+ psutil/tests/test_misc.py
+ psutil/tests/test_process.py
+ psutil/tests/test_system.py
+ psutil/tests/test_windows.py
+ scripts/*
+ setup.py
diff --git a/docs/_static/pmap-small.png b/docs/_static/pmap-small.png
new file mode 100644
index 00000000..70ed1373
--- /dev/null
+++ b/docs/_static/pmap-small.png
Binary files differ
diff --git a/docs/_static/pmap.png b/docs/_static/pmap.png
new file mode 100644
index 00000000..a92694cd
--- /dev/null
+++ b/docs/_static/pmap.png
Binary files differ
diff --git a/docs/_static/procinfo-small.png b/docs/_static/procinfo-small.png
new file mode 100644
index 00000000..39be8cc7
--- /dev/null
+++ b/docs/_static/procinfo-small.png
Binary files differ
diff --git a/docs/_static/procinfo.png b/docs/_static/procinfo.png
new file mode 100644
index 00000000..44d37972
--- /dev/null
+++ b/docs/_static/procinfo.png
Binary files differ
diff --git a/docs/_static/procsmem-small.png b/docs/_static/procsmem-small.png
new file mode 100644
index 00000000..c4f569e5
--- /dev/null
+++ b/docs/_static/procsmem-small.png
Binary files differ
diff --git a/docs/_static/procsmem.png b/docs/_static/procsmem.png
new file mode 100644
index 00000000..d7dc8c91
--- /dev/null
+++ b/docs/_static/procsmem.png
Binary files differ
diff --git a/docs/_static/top-small.png b/docs/_static/top-small.png
new file mode 100644
index 00000000..7bee953f
--- /dev/null
+++ b/docs/_static/top-small.png
Binary files differ
diff --git a/docs/_static/top.png b/docs/_static/top.png
new file mode 100644
index 00000000..fa72de87
--- /dev/null
+++ b/docs/_static/top.png
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
index 63178664..fa0b4800 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -36,6 +36,19 @@ versions from **2.6 to 3.5** (users of Python 2.4 and 2.5 may use
The psutil documentation you're reading is distributed as a single HTML page.
+Install
+-------
+
+On Windows, or on UNIX if you have a C compiler installed, the easiest way to
+install psutil is via ``pip``::
+
+ pip install psutil
+
+Alternatively, see more detailed
+`install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+instructions.
+
+
System related functions
========================
@@ -172,24 +185,24 @@ Memory
.. function:: virtual_memory()
Return statistics about system memory usage as a namedtuple including the
- following fields, expressed in bytes:
-
- - **total**: total physical memory available.
- - **available**: the actual amount of available memory that can be given
- instantly to processes that request more memory in bytes; this is
- calculated by summing different memory values depending on the platform
- (e.g. free + buffers + cached on Linux) and it is supposed to be used to
- monitor actual memory usage in a cross platform fashion.
- - **percent**: the percentage usage calculated as
- ``(total - available) / total * 100``.
- - **used**: memory used, calculated differently depending on the platform and
- designed for informational purposes only.
- - **free**: memory not being used at all (zeroed) that is readily available;
- note that this doesn't reflect the actual memory available (use 'available'
- instead).
+ following fields, expressed in bytes. Main metrics:
- Platform-specific fields:
+ - **total**: total physical memory.
+ - **available**: the memory that can be given instantly to processes without
+ the system going into swap.
+ This is calculated by summing different memory values depending on the
+ platform and it is supposed to be used to monitor actual memory usage in a
+ cross platform fashion.
+
+ Other metrics:
+ - **used**: memory used, calculated differently depending on the platform and
+ designed for informational purposes only. **total - free** does not
+ necessarily match **used**.
+ - **free**: memory not being used at all (zeroed) that is readily available;
+ note that this doesn't reflect the actual memory available (use
+ **available** instead). **total - used** does not necessarily match
+ **free**.
- **active** *(UNIX)*: memory currently in use or very recently used, and so
it is in RAM.
- **inactive** *(UNIX)*: memory that is marked as not used.
@@ -202,7 +215,7 @@ Memory
The sum of **used** and **available** does not necessarily equal **total**.
On Windows **available** and **free** are the same.
- See `scripts/meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
+ See `meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
script providing an example on how to convert bytes in a human readable form.
.. note:: if you just want to know how much physical memory is left in a
@@ -221,6 +234,10 @@ Memory
.. versionchanged:: 4.2.0 added *shared* metrics on Linux.
+ .. versionchanged:: 4.4.0 *available* and *used* values on Linux are more
+ precise and match "free" cmdline utility.
+
+
.. function:: swap_memory()
Return system swap memory statistics as a namedtuple including the following
@@ -236,7 +253,7 @@ Memory
(cumulative)
**sin** and **sout** on Windows are always set to ``0``.
- See `scripts/meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
+ See `meminfo.py <https://github.com/giampaolo/psutil/blob/master/scripts/meminfo.py>`__
script providing an example on how to convert bytes in a human readable form.
>>> import psutil
@@ -250,9 +267,12 @@ Disks
Return all mounted disk partitions as a list of namedtuples including device,
mount point and filesystem type, similarly to "df" command on UNIX. If *all*
- parameter is ``False`` return physical devices only (e.g. hard disks, cd-rom
- drives, USB keys) and ignore all others (e.g. memory partitions such as
+ parameter is ``False`` it tries to distinguish and return physical devices
+ only (e.g. hard disks, cd-rom drives, USB keys) and ignore all others
+ (e.g. memory partitions such as
`/dev/shm <http://www.cyberciti.biz/tips/what-is-devshm-and-its-practical-usage.html>`__).
+ Note that this may not be fully reliable on all systems (e.g. on BSD this
+ parameter is ignored).
Namedtuple's **fstype** field is a string which varies depending on the
platform.
On Linux it can be one of the values found in /proc/filesystems (e.g.
@@ -325,7 +345,7 @@ Disks
If *perdisk* is ``True`` return the same information for every physical disk
installed on the system as a dictionary with partition names as the keys and
the namedtuple described above as the values.
- See `scripts/iotop.py <https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py>`__
+ See `iotop.py <https://github.com/giampaolo/psutil/blob/master/scripts/iotop.py>`__
for an example application.
>>> import psutil
@@ -371,8 +391,6 @@ Network
If *pernic* is ``True`` return the same information for every network
interface installed on the system as a dictionary with network interface
names as the keys and the namedtuple described above as the values.
- See `scripts/nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
- for an example application.
>>> import psutil
>>> psutil.net_io_counters()
@@ -382,6 +400,10 @@ Network
{'lo': snetio(bytes_sent=547971, bytes_recv=547971, packets_sent=5075, packets_recv=5075, errin=0, errout=0, dropin=0, dropout=0),
'wlan0': snetio(bytes_sent=13921765, bytes_recv=62162574, packets_sent=79097, packets_recv=89648, errin=0, errout=0, dropin=0, dropout=0)}
+ Also see `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+ and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
+ for an example application.
+
.. warning::
on some systems such as Linux, on a very busy or long-lived system these
numbers may wrap (restart from zero), see
@@ -512,7 +534,8 @@ Network
snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
>>>
- See also `scripts/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
+ See also `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+ and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
for an example application.
.. note::
@@ -532,6 +555,8 @@ Network
.. versionchanged:: 3.2.0 *ptp* field was added.
+ .. versionchanged:: 4.4.0 *netmask* field on Windows is no longer ``None``.
+
.. function:: net_if_stats()
Return information about each NIC (network interface card) installed on the
@@ -546,8 +571,6 @@ Network
determined (e.g. 'localhost') it will be set to ``0``.
- **mtu**: NIC's maximum transmission unit expressed in bytes.
- See also `scripts/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
- for an example application.
Example:
>>> import psutil
@@ -555,6 +578,10 @@ 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)}
+ Also see `nettop.py <https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py>`__
+ and `ifconfig.py <https://github.com/giampaolo/psutil/blob/master/scripts/ifconfig.py>`__
+ for an example application.
+
.. versionadded:: 3.0.0
@@ -754,8 +781,9 @@ Process class
:meth:`uids`, :meth:`create_time`, ...) may be fetched by using the same
routine, but only one information is returned and the others are discarded.
When using this context manager the internal routine is executed once (in
- the example below on :meth:`name()`) and the other info are cached and
- returned in the sub-sequent calls sharing the same internal routine.
+ the example below on :meth:`name()`) and the other info are cached.
+ The subsequent calls sharing the same internal routine will return the
+ cached value.
The cache is cleared when exiting the context manager block.
The advice is to use this every time you retrieve more than one information
about the process. If you're lucky, you'll get a hell of a speedup.
@@ -768,53 +796,55 @@ Process class
... p.cpu_times() # return cached value
... p.cpu_percent() # return cached value
... p.create_time() # return cached value
+ ... p.ppid() # return cached value
+ ... p.status() # return cached value
...
>>>
Here's a list of methods which can take advantage of the speedup depending
on what platform you're on.
- In the table below horizontal emtpy rows indicate what process methods can
- be efficiently grouped together internally.
+ In the table below horizontal emtpy rows delimitate what process methods
+ can be efficiently grouped together internally.
The last column (speedup) shows an approximation of the speedup you can get
- if you collect all this methods together (best case scenario).
-
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | Linux | Windows | OSX | BSD | SunOS |
- +==============================+==============================+=======+==============================+==========================+
- | :meth:`~Process.cpu_percent` | :meth:`cpu_affinity` | | :meth:`~Process.cpu_percent` | :meth:`name` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_percent` | | :meth:`~Process.cpu_times` | :meth:`cmdline` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`create_time` | :meth:`~Process.cpu_times` | | :meth:`create_time` | |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`name` | :meth:`io_counters` | | :meth:`gids` | :meth:`create_time` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`ppid` | :meth:`ionice` | | :meth:`io_counters` | :meth:`memory_full_info` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`status` | :meth:`memory_info` | | :meth:`memory_full_info` | :meth:`memory_info` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`terminal` | :meth:`memory_percent` | | :meth:`memory_info` | :meth:`memory_percent` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | | :meth:`nice` | | :meth:`memory_percent` | :meth:`nice` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`gids` | :meth:`num_handles` | | :meth:`num_ctx_switches` | :meth:`num_threads` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`num_ctx_switches` | | | :meth:`ppid` | :meth:`ppid` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`num_threads` | | | :meth:`status` | :meth:`status` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | :meth:`uids` | | | :meth:`terminal` | :meth:`terminal` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | | | | :meth:`uids` | |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | | | | | :meth:`gids` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | | | | | :meth:`uids` |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | | | | | |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
- | *speedup: +2.5x* | | | *speedup: +2x* | |
- +------------------------------+------------------------------+-------+------------------------------+--------------------------+
+ if you call all the methods together (best case scenario).
+
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | Linux | Windows | OSX | BSD | SunOS |
+ +==============================+=============+==============================+==============================+==========================+
+ | :meth:`~Process.cpu_percent` | | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`name` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`~Process.cpu_times` | | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`cmdline` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`create_time` | | :meth:`memory_info` | :meth:`create_time` | :meth:`create_time` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`name` | | :meth:`memory_percent` | :meth:`gids` | |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`ppid` | | :meth:`num_ctx_switches` | :meth:`io_counters` | :meth:`memory_info` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`status` | | :meth:`num_threads` | :meth:`name` | :meth:`memory_percent` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`terminal` | | | :meth:`memory_info` | :meth:`nice` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | | | :meth:`create_time` | :meth:`memory_percent` | :meth:`num_threads` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`gids` | | :meth:`gids` | :meth:`num_ctx_switches` | :meth:`ppid` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`num_ctx_switches` | | :meth:`name` | :meth:`ppid` | :meth:`status` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`num_threads` | | :meth:`ppid` | :meth:`status` | :meth:`terminal` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`uids` | | :meth:`status` | :meth:`terminal` | |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | :meth:`username` | | :meth:`terminal` | :meth:`uids` | :meth:`gids` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | | | :meth:`uids` | :meth:`username` | :meth:`uids` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | | | :meth:`username` | | :meth:`username` |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | | | | | |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
+ | *speedup: +2.5x* | | *speedup: +1.9x* | *speedup: +2x* | |
+ +------------------------------+-------------+------------------------------+------------------------------+--------------------------+
.. versionadded:: 5.0.0
@@ -885,7 +915,7 @@ Process class
3.0.0 *ad_value* is used also when incurring into
:class:`ZombieProcess` exception, not only :class:`AccessDenied`
- .. versionchanged:: 4.3.0 :meth:`as_dict` is considerably faster thanks
+ .. versionchanged:: 5.0.0 :meth:`as_dict` is considerably faster thanks
to :meth:`oneshot` context manager.
.. method:: parent()
@@ -996,12 +1026,14 @@ Process class
Get or set process resource limits (see
`man prlimit <http://linux.die.net/man/2/prlimit>`__). *resource* is one
- of the :data:`psutil.RLIMIT_* <psutil.RLIMIT_INFINITY>` constants.
+ of the :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>` constants.
*limits* is a ``(soft, hard)`` tuple.
This is the same as `resource.getrlimit() <http://docs.python.org/library/resource.html#resource.getrlimit>`__
and `resource.setrlimit() <http://docs.python.org/library/resource.html#resource.setrlimit>`__
but can be used for any process PID, not only
`os.getpid() <http://docs.python.org/library/os.html#os.getpid>`__.
+ For get, return value is a ``(soft, hard)`` tuple. Each value may be either
+ and integer or :data:`psutil.RLIMIT_* <psutil.RLIM_INFINITY>`.
Example:
>>> import psutil
@@ -1218,7 +1250,11 @@ Process class
- **dirty** *(Linux)*: the number of dirty pages.
- For Windows fields rely on
+ - **pfaults** *(OSX)*: number of page faults.
+
+ - **pageins** *(OSX)*: number of actual pageins.
+
+ For on explanation of Windows fields rely on
`PROCESS_MEMORY_COUNTERS_EX <http://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx>`__ structure doc.
Example on Linux:
@@ -1277,7 +1313,7 @@ Process class
pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
>>>
- See also `scripts/procsmem.py <https://github.com/giampaolo/psutil/blob/master/scripts/procsmem.py>`__
+ See also `procsmem.py <https://github.com/giampaolo/psutil/blob/master/scripts/procsmem.py>`__
for an example application.
.. versionadded:: 4.0.0
@@ -1306,7 +1342,7 @@ Process class
is ``False`` each mapped region is shown as a single entity and the
namedtuple will also include the mapped region's address space (*addr*)
and permission set (*perms*).
- See `scripts/pmap.py <https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py>`__
+ See `pmap.py <https://github.com/giampaolo/psutil/blob/master/scripts/pmap.py>`__
for an example application.
+---------------+--------------+---------+-----------+--------------+
@@ -1506,9 +1542,8 @@ Process class
`signal module <http://docs.python.org//library/signal.html>`__
constants) preemptively checking whether PID has been reused.
On UNIX this is the same as ``os.kill(pid, sig)``.
- On Windows only **SIGTERM**, **CTRL_C_EVENT** and **CTRL_BREAK_EVENT**
- signals are supported and **SIGTERM** is treated as an alias for
- :meth:`kill()`.
+ On Windows only *SIGTERM*, *CTRL_C_EVENT* and *CTRL_BREAK_EVENT* signals
+ are supported and *SIGTERM* is treated as an alias for :meth:`kill()`.
.. versionchanged::
3.2.0 support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals on Windows
@@ -1516,28 +1551,28 @@ Process class
.. method:: suspend()
- Suspend process execution with **SIGSTOP** signal preemptively checking
+ Suspend process execution with *SIGSTOP* signal preemptively checking
whether PID has been reused.
On UNIX this is the same as ``os.kill(pid, signal.SIGSTOP)``.
On Windows this is done by suspending all process threads execution.
.. method:: resume()
- Resume process execution with **SIGCONT** signal preemptively checking
+ Resume process execution with *SIGCONT* signal preemptively checking
whether PID has been reused.
On UNIX this is the same as ``os.kill(pid, signal.SIGCONT)``.
On Windows this is done by resuming all process threads execution.
.. method:: terminate()
- Terminate the process with **SIGTERM** signal preemptively checking
+ Terminate the process with *SIGTERM* signal preemptively checking
whether PID has been reused.
On UNIX this is the same as ``os.kill(pid, signal.SIGTERM)``.
On Windows this is an alias for :meth:`kill`.
.. method:: kill()
- Kill the current process by using **SIGKILL** signal preemptively
+ Kill the current process by using *SIGKILL* signal preemptively
checking whether PID has been reused.
On UNIX this is the same as ``os.kill(pid, signal.SIGKILL)``.
On Windows this is done by using
@@ -1563,10 +1598,10 @@ Popen class
A more convenient interface to stdlib
`subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__.
- It starts a sub process and deals with it exactly as when using
+ It starts a sub process and you deal with it exactly as when using
`subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__
- but in addition it also provides all the methods of
- :class:`psutil.Process` class in a single interface.
+ but in addition it also provides all the methods of :class:`psutil.Process`
+ class.
For method names common to both classes such as
:meth:`send_signal() <psutil.Process.send_signal()>`,
:meth:`terminate() <psutil.Process.terminate()>` and
@@ -1599,6 +1634,17 @@ Popen class
0
>>>
+ :class:`psutil.Popen` objects are supported as context managers via the with
+ statement: on exit, standard file descriptors are closed, and the process
+ is waited for. This is supported on all Python versions.
+
+ >>> import psutil, subprocess
+ >>> with psutil.Popen(["ifconfig"], stdout=subprocess.PIPE) as proc:
+ >>> log.write(proc.stdout.read())
+
+
+ .. versionchanged:: 4.4.0 added context manager support
+
Windows services
================
@@ -1813,7 +1859,7 @@ Constants
instead of a plain integer.
.. _const-rlimit:
-.. data:: RLIMIT_INFINITY
+.. data:: RLIM_INFINITY
RLIMIT_AS
RLIMIT_CORE
RLIMIT_CPU
@@ -1828,7 +1874,6 @@ Constants
RLIMIT_RSS
RLIMIT_RTPRIO
RLIMIT_RTTIME
- RLIMIT_RTPRIO
RLIMIT_SIGPENDING
RLIMIT_STACK
@@ -1865,3 +1910,55 @@ Development guide
If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug)
take a look at the
`development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_.
+
+
+Timeline
+========
+
+- 2016-10-23: `psutil-4.4.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.4.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#441>`__
+- 2016-10-23: `psutil-4.4.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.4.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#440>`__
+- 2016-09-01: `psutil-4.3.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.3.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#431>`__
+- 2016-06-18: `psutil-4.3.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.3.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#430>`__
+- 2016-05-15: `psutil-4.2.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.2.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#420>`__
+- 2016-03-12: `psutil-4.1.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.1.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#410>`__
+- 2016-02-17: `psutil-4.0.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=4.0.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#400>`__
+- 2016-01-20: `psutil-3.4.2.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.4.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#342>`__
+- 2016-01-15: `psutil-3.4.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.4.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#341>`__
+- 2015-11-25: `psutil-3.3.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.3.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#330>`__
+- 2015-10-04: `psutil-3.2.2.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.2.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#322>`__
+- 2015-09-03: `psutil-3.2.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.2.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#321>`__
+- 2015-09-02: `psutil-3.2.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.2.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#320>`__
+- 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.1.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#311>`__
+- 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.1.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#310>`__
+- 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.0.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#301>`__
+- 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=3.0.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#300>`__
+- 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.2.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#221>`__
+- 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.2.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#220>`__
+- 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#213>`__
+- 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.1.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#212>`__
+- 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.1.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#211>`__
+- 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.1.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#210>`__
+- 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=2.0.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#200>`__
+- 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.2.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#121>`__
+- 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.2.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#120>`__
+- 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.1.3&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#113>`__
+- 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.1.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#112>`__
+- 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.1.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#111>`__
+- 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.1.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#110>`__
+- 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.0.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#101>`__
+- 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=1.0.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#100>`__
+- 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.7.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#071>`__
+- 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.7.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#070>`__
+- 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.6.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#061>`__
+- 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.6.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#060>`__
+- 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.5.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#051>`__
+- 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.5.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#050>`__
+- 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.4.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#041>`__
+- 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.4.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#040>`__
+- 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.3.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#030>`__
+- 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.2.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#021>`__
+- 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.2.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#020>`__
+- 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.1.3&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#013>`__
+- 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.1.2&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#012>`__
+- 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.1.1&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#011>`__
+- 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/pypi?name=psutil&version=0.1.0&:action=files>`__ - `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#010>`__
diff --git a/docs/make.bat b/docs/make.bat
index 9bc67515..185fc951 100644
--- a/docs/make.bat
+++ b/docs/make.bat
@@ -1,242 +1,32 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\psutil.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\psutil.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
+@echo off
+
+rem ==========================================================================
+rem Shortcuts for various tasks, emulating UNIX "make" on Windows.
+rem It is primarly intended as a shortcut for compiling / installing
+rem psutil ("make.bat build", "make.bat install") and running tests
+rem ("make.bat test").
+rem
+rem This script is modeled after my Windows installation which uses:
+rem - Visual studio 2008 for Python 2.6, 2.7, 3.2
+rem - Visual studio 2010 for Python 3.3+
+rem ...therefore it might not work on your Windows installation.
+rem
+rem By default C:\Python27\python.exe is used.
+rem To compile for a specific Python version run:
+rem set PYTHON=C:\Python34\python.exe & make.bat build
+rem
+rem To use a different test script:
+rem set PYTHON=C:\Python34\python.exe & set TSCRIPT=foo.py & make.bat test
+rem ==========================================================================
+
+if "%PYTHON%" == "" (
+ set PYTHON=C:\Python27\python.exe
+)
+if "%TSCRIPT%" == "" (
+ set TSCRIPT=psutil\tests\runner.py
+)
+
+rem Needed to locate the .pypirc file and upload exes on PYPI.
+set HOME=%USERPROFILE%
+
+%PYTHON% scripts\internal\winmake.py %1 %2
diff --git a/make.bat b/make.bat
index 6114afc0..185fc951 100644
--- a/make.bat
+++ b/make.bat
@@ -26,226 +26,7 @@ if "%TSCRIPT%" == "" (
set TSCRIPT=psutil\tests\runner.py
)
-set VSINSTALLDIR=%VS90COMNTOOLS%..\..
-
-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% %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%
-rem ==========================================================================
-
-if "%1" == "help" (
- :help
- echo Run `make ^<target^>` where ^<target^> is one of:
- echo build compile without installing
- echo build-all build exes + wheels
- echo clean clean build files
- echo flake8 run flake8
- echo install compile and install
- echo setup-dev-env install/upgrade pip, pywin32, wheels, etc.
- echo setup-dev-env-all same as above, for all python versions
- echo test run tests
- echo test-memleaks run memory leak tests
- echo test-process run process related tests
- echo test-system run system APIs related tests
- echo test-platform platform-specific Windows tests
- echo uninstall uninstall
- echo upload-all upload exes + wheels
- goto :eof
-)
-
-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 (*.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)
- if exist psutil.egg-info (rmdir /S /Q psutil.egg-info)
- if exist build (rmdir /S /Q build)
- if exist dist (rmdir /S /Q dist)
- goto :eof
-)
-
-if "%1" == "build" (
- :build
- "%VSINSTALLDIR%\VC\bin\vcvars64.bat"
- %PYTHON% setup.py build
- if %errorlevel% neq 0 goto :error
- rem copies *.pyd files in ./psutil directory in order to allow
- rem "import psutil" when using the interactive interpreter from
- rem within this directory.
- %PYTHON% setup.py build_ext -i
- if %errorlevel% neq 0 goto :error
- goto :eof
-)
-
-if "%1" == "install" (
- :install
- call :build
- %PYTHON% setup.py develop
- goto :eof
-)
-
-if "%1" == "uninstall" (
- for %%A in ("%PYTHON%") do (
- set folder=%%~dpA
- )
- for /F "delims=" %%i in ('dir /b %folder%\Lib\site-packages\*psutil*') do (
- rmdir /S /Q %folder%\Lib\site-packages\%%i
- )
- goto :eof
-)
-
-if "%1" == "test" (
- call :install
- %PYTHON% %TSCRIPT%
- goto :eof
-)
-
-if "%1" == "test-process" (
- call :install
- %PYTHON% -m unittest -v psutil.tests.test_process
- goto :eof
-)
-
-if "%1" == "test-system" (
- call :install
- %PYTHON% -m unittest -v psutil.tests.test_system
- goto :eof
-)
-
-if "%1" == "test-platform" (
- call :install
- %PYTHON% psutil\tests\test_windows.py
- goto :eof
-)
-
-if "%1" == "test-by-name" (
- call :install
- %PYTHON% -m nose psutil\tests\test_process.py psutil\tests\test_system.py psutil\tests\test_windows.py psutil\tests\test_misc.py --nocapture -v -m %2
- goto :eof
-)
-
-if "%1" == "test-memleaks" (
- call :install
- %PYTHON% test\test_memory_leaks.py
- goto :eof
-)
-
-if "%1" == "build-all" (
- :build-all
- "%VSINSTALLDIR%\VC\bin\vcvars64.bat"
- for %%P in (%ALL_PYTHONS%) do (
- @echo ------------------------------------------------
- @echo building exe for %%P
- @echo ------------------------------------------------
- %%P setup.py build bdist_wininst || goto :error
- @echo ------------------------------------------------
- @echo building wheel for %%P
- @echo ------------------------------------------------
- %%P setup.py build bdist_wheel || goto :error
- )
- echo OK
- goto :eof
-)
-
-if "%1" == "upload-all" (
- :upload-exes
- "%VSINSTALLDIR%\VC\bin\vcvars64.bat"
- for %%P in (%ALL_PYTHONS%) do (
- @echo ------------------------------------------------
- @echo uploading exe for %%P
- @echo ------------------------------------------------
- %%P setup.py build bdist_wininst upload || goto :error
- @echo ------------------------------------------------
- @echo uploading wheel for %%P
- @echo ------------------------------------------------
- %%P setup.py build bdist_wheel upload || goto :error
- )
- echo OK
- goto :eof
-)
-
-if "%1" == "setup-dev-env" (
- :setup-env
- if not exist get-pip.py (
- @echo ------------------------------------------------
- @echo downloading pip installer
- @echo ------------------------------------------------
- C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); open('get-pip.py', 'wb').write(r.read())"
- )
- @echo ------------------------------------------------
- @echo installing pip for %PYTHON%
- @echo ------------------------------------------------
- %PYTHON% get-pip.py
- @echo ------------------------------------------------
- @echo upgrade pip for %PYTHON%
- @echo ------------------------------------------------
- %PYTHON% -m pip install pip --upgrade
- @echo ------------------------------------------------
- @echo installing deps
- @echo ------------------------------------------------
- rem mandatory / for unittests
- %PYTHON% -m pip install unittest2 ipaddress mock wmi wheel pypiwin32 --upgrade
- rem nice to have
- rem %PYTHON% -m pip install ipdb nose --upgrade
- goto :eof
-)
-
-if "%1" == "setup-dev-env-all" (
- :setup-env
- if not exist get-pip.py (
- @echo ------------------------------------------------
- @echo downloading pip installer
- @echo ------------------------------------------------
- C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); open('get-pip.py', 'wb').write(r.read())"
- )
- for %%P in (%ALL_PYTHONS%) do (
- @echo ------------------------------------------------
- @echo installing pip for %%P
- @echo ------------------------------------------------
- %%P get-pip.py
- @echo ------------------------------------------------
- @echo installing deps for %%P
- @echo ------------------------------------------------
- rem mandatory / for unittests
- %%P -m pip install unittest2 ipaddress mock wmi wheel pypiwin32 --upgrade
- rem nice to have
- rem %%P -m pip install ipdb nose --upgrade
- )
- goto :eof
-)
-
-if "%1" == "flake8" (
- :flake8
- %PYTHON% -c "from flake8.main import main; main()"
- goto :eof
-)
-
-if "%1" == "bench-oneshot" (
- call :install
- %PYTHON% scripts\internal\bench_oneshot.py
- goto :eof
-)
-
-goto :help
-
-:error
- @echo ------------------------------------------------
- @echo last command exited with error code %errorlevel%
- @echo ------------------------------------------------
- @exit /b %errorlevel%
- goto :eof
+%PYTHON% scripts\internal\winmake.py %1 %2
diff --git a/psutil/__init__.py b/psutil/__init__.py
index dccf6644..0b5e496d 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -29,6 +29,7 @@ except ImportError:
from . import _common
from ._common import deprecated_method
from ._common import memoize
+from ._common import memoize_when_activated
from ._compat import callable
from ._compat import long
from ._compat import PY3 as _PY3
@@ -188,7 +189,7 @@ __all__ = [
]
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "4.3.1"
+__version__ = "4.4.1"
version_info = tuple([int(num) for num in __version__.split('.')])
AF_LINK = _psplatform.AF_LINK
_TOTAL_PHYMEM = None
@@ -391,14 +392,13 @@ class Process(object):
try:
self.create_time()
except AccessDenied:
- # we should never get here as AFAIK we're able to get
+ # We should never get here as AFAIK we're able to get
# process creation time on all platforms even as a
- # limited user
+ # limited user.
pass
except ZombieProcess:
- # Let's consider a zombie process as legitimate as
- # tehcnically it's still alive (it can be queried,
- # although not always, and it's returned by pids()).
+ # Zombies can still be queried by this class (although
+ # not always) and pids() return them so just go on.
pass
except NoSuchProcess:
if not _ignore_nsp:
@@ -464,13 +464,15 @@ class Process(object):
one information about the process. If you're lucky, you'll
get a hell of a speedup.
- >>> p = Process()
+ >>> import psutil
+ >>> p = psutil.Process()
>>> with p.oneshot():
- ... p.name() # execute internal routine
- ... p.ppid() # use cached value
- ... p.uids() # use cached value
- ... p.gids() # use cached value
+ ... p.name() # collect multiple info
+ ... p.cpu_times() # return cached value
+ ... p.cpu_percent() # return cached value
+ ... p.create_time() # return cached value
...
+ >>>
"""
if self._oneshot_inctx:
# NOOP: this covers the use case where the user enters the
@@ -486,11 +488,26 @@ class Process(object):
else:
self._oneshot_inctx = True
try:
+ # cached in case cpu_percent() is used
+ self.cpu_times.cache_activate()
+ # 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
finally:
- self._oneshot_inctx = False
+ self.cpu_times.cache_deactivate()
+ self.memory_info.cache_deactivate()
+ self.ppid.cache_deactivate()
+ if POSIX:
+ self.uids.cache_deactivate()
self._proc.oneshot_exit()
+ self._oneshot_inctx = False
def as_dict(self, attrs=None, ad_value=None):
"""Utility method returning process information as a
@@ -581,6 +598,7 @@ class Process(object):
"""The process PID."""
return self._pid
+ @memoize_when_activated
def ppid(self):
"""The process parent PID.
On Windows the return value is cached after first call.
@@ -716,6 +734,7 @@ class Process(object):
if POSIX:
+ @memoize_when_activated
def uids(self):
"""Return process UIDs as a (real, effective, saved)
namedtuple.
@@ -967,13 +986,17 @@ class Process(object):
>>>
"""
blocking = interval is not None and interval > 0.0
+ if interval is not None and interval < 0:
+ raise ValueError("interval is not positive (got %r)" % interval)
num_cpus = cpu_count() or 1
+
if POSIX:
def timer():
return _timer() * num_cpus
else:
def timer():
return sum(cpu_times())
+
if blocking:
st1 = timer()
pt1 = self._proc.cpu_times()
@@ -1023,6 +1046,7 @@ class Process(object):
single_cpu_percent = overall_cpus_percent * num_cpus
return round(single_cpu_percent, 1)
+ @memoize_when_activated
def cpu_times(self):
"""Return a (user, system, children_user, children_system)
namedtuple representing the accumulated process time, in
@@ -1033,6 +1057,7 @@ class Process(object):
"""
return self._proc.cpu_times()
+ @memoize_when_activated
def memory_info(self):
"""Return a namedtuple with variable fields depending on the
platform, representing memory information about the process.
@@ -1154,6 +1179,7 @@ class Process(object):
if POSIX:
def _send_signal(self, sig):
+ assert not self.pid < 0, self.pid
if self.pid == 0:
# see "man 2 kill"
raise ValueError(
@@ -1306,6 +1332,27 @@ class Popen(Process):
def __dir__(self):
return sorted(set(dir(Popen) + dir(subprocess.Popen)))
+ def __enter__(self):
+ if hasattr(self.__subproc, '__enter__'):
+ self.__subproc.__enter__()
+ return self
+
+ def __exit__(self, *args, **kwargs):
+ if hasattr(self.__subproc, '__exit__'):
+ return self.__subproc.__exit__(*args, **kwargs)
+ else:
+ if self.stdout:
+ self.stdout.close()
+ if self.stderr:
+ self.stderr.close()
+ try:
+ # Flushing a BufferedWriter may raise an error.
+ if self.stdin:
+ self.stdin.close()
+ finally:
+ # Wait for the process to terminate, to avoid zombies.
+ self.wait()
+
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
@@ -1612,6 +1659,8 @@ def cpu_percent(interval=None, percpu=False):
global _last_cpu_times
global _last_per_cpu_times
blocking = interval is not None and interval > 0.0
+ if interval is not None and interval < 0:
+ raise ValueError("interval is not positive (got %r)" % interval)
def calculate(t1, t2):
t1_all = sum(t1)
@@ -1685,6 +1734,8 @@ def cpu_times_percent(interval=None, percpu=False):
global _last_cpu_times_2
global _last_per_cpu_times_2
blocking = interval is not None and interval > 0.0
+ if interval is not None and interval < 0:
+ raise ValueError("interval is not positive (got %r)" % interval)
def calculate(t1, t2):
nums = []
@@ -1764,12 +1815,11 @@ def virtual_memory():
total physical memory available.
- available:
- the actual amount of available memory that can be given
- instantly to processes that request more memory in bytes; this
- is calculated by summing different memory values depending on
- the platform (e.g. free + buffers + cached on Linux) and it is
- supposed to be used to monitor actual memory usage in a cross
- platform fashion.
+ the memory that can be given instantly to processes without the
+ system going into swap.
+ This is calculated by summing different memory values depending
+ on the platform and it is supposed to be used to monitor actual
+ memory usage in a cross platform fashion.
- percent:
the percentage usage calculated as (total - available) / total * 100
@@ -2121,7 +2171,7 @@ def test(): # pragma: no cover
pinfo['name'].strip() or '?'))
-del memoize, division, deprecated_method
+del memoize, memoize_when_activated, division, deprecated_method
if sys.version_info[0] < 3:
del num, x
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index 1f1909bb..5f8742df 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -121,6 +121,7 @@ kinfo_proc_map = dict(
memtext=20,
memdata=21,
memstack=22,
+ name=23,
)
@@ -192,8 +193,7 @@ def virtual_memory():
def swap_memory():
"""System swap memory as (total, used, free, sin, sout) namedtuple."""
- pagesize = 1 if OPENBSD else PAGESIZE
- total, used, free, sin, sout = [x * pagesize for x in cext.swap_mem()]
+ total, used, free, sin, sout = cext.swap_mem()
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
@@ -310,15 +310,14 @@ def cpu_stats():
def disk_partitions(all=False):
+ """Return mounted disk partitions as a list of namedtuples.
+ 'all' argument is ignored, see:
+ https://github.com/giampaolo/psutil/issues/906
+ """
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
- if device == 'none':
- device = ''
- if not all:
- if not os.path.isabs(device) or not os.path.exists(device):
- continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
@@ -342,7 +341,9 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext_posix.net_if_duplex_speed(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
@@ -440,6 +441,11 @@ def wrap_exceptions(fun):
try:
return fun(self, *args, **kwargs)
except OSError as err:
+ if self.pid == 0:
+ if 0 in pids():
+ raise AccessDenied(self.pid, self._name)
+ else:
+ raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
@@ -494,7 +500,8 @@ class Process(object):
@wrap_exceptions
def name(self):
- return cext.proc_name(self.pid)
+ name = self.oneshot()[kinfo_proc_map['name']]
+ return name if name is not None else cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
@@ -521,7 +528,7 @@ class Process(object):
@wrap_exceptions
def cmdline(self):
if OPENBSD and self.pid == 0:
- return None # ...else it crashes
+ return [] # ...else it crashes
elif NETBSD:
# XXX - most of the times the underlying sysctl() call on Net
# and Open BSD returns a truncated string.
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 2f13cda2..b1071fd5 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -289,62 +289,167 @@ except Exception:
# =====================================================================
+def calculate_avail_vmem(mems):
+ """Fallback for kernels < 3.14 where /proc/meminfo does not provide
+ "MemAvailable:" column (see: https://blog.famzah.net/2014/09/24/).
+ This code reimplements the algorithm outlined here:
+ https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+ commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+
+ XXX: on recent kernels this calculation differs by ~1.5% than
+ "MemAvailable:" as it's calculated slightly differently, see:
+ https://gitlab.com/procps-ng/procps/issues/42
+ https://github.com/famzah/linux-memavailable-procfs/issues/2
+ It is still way more realistic than doing (free + cached) though.
+ """
+ # Fallback for very old distros. According to
+ # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+ # commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+ # ...long ago "avail" was calculated as (free + cached).
+ # We might fallback in such cases:
+ # "Active(file)" not available: 2.6.28 / Dec 2008
+ # "Inactive(file)" not available: 2.6.28 / Dec 2008
+ # "SReclaimable:" not available: 2.6.19 / Nov 2006
+ # /proc/zoneinfo not available: 2.6.13 / Aug 2005
+ free = mems[b'MemFree:']
+ fallback = free + mems.get(b"Cached:", 0)
+ try:
+ lru_active_file = mems[b'Active(file):']
+ lru_inactive_file = mems[b'Inactive(file):']
+ slab_reclaimable = mems[b'SReclaimable:']
+ except KeyError:
+ return fallback
+ try:
+ f = open_binary('%s/zoneinfo' % get_procfs_path())
+ except IOError:
+ return fallback # kernel 2.6.13
+
+ watermark_low = 0
+ with f:
+ for line in f:
+ line = line.strip()
+ if line.startswith(b'low'):
+ watermark_low += int(line.split()[1])
+ watermark_low *= PAGESIZE
+ watermark_low = watermark_low
+
+ avail = free - watermark_low
+ pagecache = lru_active_file + lru_inactive_file
+ pagecache -= min(pagecache / 2, watermark_low)
+ avail += pagecache
+ avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low)
+ return int(avail)
+
+
def virtual_memory():
- total, free, buffers, shared, _, _, unit_multiplier = cext.linux_sysinfo()
- total *= unit_multiplier
- free *= unit_multiplier
- buffers *= unit_multiplier
- # Note: this (on my Ubuntu 14.04, kernel 3.13 at least) may be 0.
- # If so, it will be determined from /proc/meminfo.
- shared *= unit_multiplier or None
- if shared == 0:
- shared = None
-
- cached = active = inactive = None
+ """Report virtual memory stats.
+ This implementation matches "free" and "vmstat -s" cmdline
+ utility values and procps-ng-3.3.12 source was used as a reference
+ (2016-09-18):
+ https://gitlab.com/procps-ng/procps/blob/
+ 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c
+ For reference, procps-ng-3.3.10 is the version available on Ubuntu
+ 16.04.
+
+ Note about "available" memory: up until psutil 4.3 it was
+ calculated as "avail = (free + buffers + cached)". Now
+ "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as
+ it's more accurate.
+ That matches "available" column in newer versions of "free".
+ """
+ missing_fields = []
+ mems = {}
with open_binary('%s/meminfo' % get_procfs_path()) as f:
for line in f:
- if cached is None and line.startswith(b"Cached:"):
- cached = int(line.split()[1]) * 1024
- elif active is None and line.startswith(b"Active:"):
- active = int(line.split()[1]) * 1024
- elif inactive is None and line.startswith(b"Inactive:"):
- inactive = int(line.split()[1]) * 1024
- # From "man free":
- # The shared memory column represents either the MemShared
- # value (2.4 kernels) or the Shmem value (2.6+ kernels) taken
- # from the /proc/meminfo file. The value is zero if none of
- # the entries is exported by the kernel.
- elif shared is None and \
- line.startswith(b"MemShared:") or \
- line.startswith(b"Shmem:"):
- shared = int(line.split()[1]) * 1024
-
- missing = []
- if cached is None:
- missing.append('cached')
+ fields = line.split()
+ mems[fields[0]] = int(fields[1]) * 1024
+
+ # /proc doc states that the available fields in /proc/meminfo vary
+ # by architecture and compile options, but these 3 values are also
+ # returned by sysinfo(2); as such we assume they are always there.
+ total = mems[b'MemTotal:']
+ free = mems[b'MemFree:']
+ buffers = mems[b'Buffers:']
+
+ try:
+ cached = mems[b"Cached:"]
+ except KeyError:
cached = 0
- if active is None:
- missing.append('active')
+ missing_fields.append('cached')
+ else:
+ # "free" cmdline utility sums reclaimable to cached.
+ # Older versions of procps used to add slab memory instead.
+ # This got changed in:
+ # https://gitlab.com/procps-ng/procps/commit/
+ # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+ cached += mems.get(b"SReclaimable:", 0) # since kernel 2.6.19
+
+ try:
+ shared = mems[b'Shmem:'] # since kernel 2.6.32
+ except KeyError:
+ try:
+ shared = mems[b'MemShared:'] # kernels 2.4
+ except KeyError:
+ shared = 0
+ missing_fields.append('shared')
+
+ try:
+ active = mems[b"Active:"]
+ except KeyError:
active = 0
- if inactive is None:
- missing.append('inactive')
- inactive = 0
- if shared is None:
- missing.append('shared')
- shared = 0
- if missing:
+ missing_fields.append('active')
+
+ try:
+ inactive = mems[b"Inactive:"]
+ except KeyError:
+ try:
+ inactive = \
+ mems[b"Inact_dirty:"] + \
+ mems[b"Inact_clean:"] + \
+ mems[b"Inact_laundry:"]
+ except KeyError:
+ inactive = 0
+ missing_fields.append('inactive')
+
+ used = total - free - cached - buffers
+ if used < 0:
+ # May be symptomatic of running within a LCX container where such
+ # values will be dramatically distorted over those of the host.
+ used = total - free
+
+ # - starting from 4.4.0 we match free's "available" column.
+ # Before 4.4.0 we calculated it as (free + buffers + cached)
+ # which matched htop.
+ # - free and htop available memory differs as per:
+ # http://askubuntu.com/a/369589
+ # http://unix.stackexchange.com/a/65852/168884
+ # - MemAvailable has been introduced in kernel 3.14
+ try:
+ avail = mems[b'MemAvailable:']
+ except KeyError:
+ avail = calculate_avail_vmem(mems)
+
+ if avail < 0:
+ avail = 0
+ missing_fields.append('available')
+
+ # If avail is greater than total or our calculation overflows,
+ # that's symptomatic of running within a LCX container where such
+ # values will be dramatically distorted over those of the host.
+ # https://gitlab.com/procps-ng/procps/blob/
+ # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
+ if avail > total:
+ avail = free
+
+ percent = usage_percent((total - avail), total, _round=1)
+
+ # Warn about missing metrics which are set to 0.
+ if missing_fields:
msg = "%s memory stats couldn't be determined and %s set to 0" % (
- ", ".join(missing),
- "was" if len(missing) == 1 else "were")
+ ", ".join(missing_fields),
+ "was" if len(missing_fields) == 1 else "were")
warnings.warn(msg, RuntimeWarning)
- # Note: this value matches "htop" perfectly.
- avail = free + buffers + cached
- # Note: this value matches "free", but not all the time, see:
- # https://github.com/giampaolo/psutil/issues/685#issuecomment-202914057
- used = total - free
- # Note: this value matches "htop" perfectly.
- percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, buffers, cached, shared)
@@ -588,7 +693,8 @@ class Connections:
raise
return inodes
- def decode_address(self, addr, family):
+ @staticmethod
+ def decode_address(addr, family):
"""Accept an "ip:port" address as displayed in /proc/net/*
and convert it into a human readable form, like:
@@ -642,7 +748,8 @@ class Connections:
raise
return (ip, port)
- def process_inet(self, file, family, type_, inodes, filter_pid=None):
+ @staticmethod
+ def process_inet(file, family, type_, inodes, filter_pid=None):
"""Parse /proc/net/tcp* and /proc/net/udp* files."""
if file.endswith('6') and not os.path.exists(file):
# IPv6 not supported
@@ -675,13 +782,14 @@ class Connections:
else:
status = _common.CONN_NONE
try:
- laddr = self.decode_address(laddr, family)
- raddr = self.decode_address(raddr, family)
+ laddr = Connections.decode_address(laddr, family)
+ raddr = Connections.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):
+ @staticmethod
+ def process_unix(file, family, inodes, filter_pid=None):
"""Parse /proc/net/unix files."""
with open_text(file, buffering=BIGGER_FILE_BUFFERING) as f:
f.readline() # skip the first line
@@ -768,14 +876,26 @@ def net_io_counters():
assert colon > 0, repr(line)
name = line[:colon].strip()
fields = line[colon + 1:].strip().split()
- bytes_recv = int(fields[0])
- packets_recv = int(fields[1])
- errin = int(fields[2])
- dropin = int(fields[3])
- bytes_sent = int(fields[8])
- packets_sent = int(fields[9])
- errout = int(fields[10])
- dropout = int(fields[11])
+
+ # in
+ (bytes_recv,
+ packets_recv,
+ errin,
+ dropin,
+ fifoin, # unused
+ framein, # unused
+ compressedin, # unused
+ multicastin, # unused
+ # out
+ bytes_sent,
+ packets_sent,
+ errout,
+ dropout,
+ fifoout, # unused
+ collisionsout, # unused
+ carrierout, # unused
+ compressedout) = map(int, fields)
+
retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv,
errin, errout, dropin, dropout)
return retdict
@@ -789,9 +909,10 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- isup, duplex, speed, mtu = cext.net_if_stats(name)
- duplex = duplex_map[duplex]
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext.net_if_duplex_speed(name)
+ ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
return ret
@@ -1335,13 +1456,14 @@ class Process(object):
def cpu_affinity_set(self, cpus):
try:
cext.proc_cpu_affinity_set(self.pid, cpus)
- except OSError as err:
- if err.errno == errno.EINVAL:
+ except (OSError, ValueError) as err:
+ if isinstance(err, ValueError) or err.errno == errno.EINVAL:
allcpus = tuple(range(len(per_cpu_times())))
for cpu in cpus:
if cpu not in allcpus:
- raise ValueError("invalid CPU #%i (choose between %s)"
- % (cpu, allcpus))
+ raise ValueError(
+ "invalid CPU number %r; choose between %s" % (
+ cpu, allcpus))
raise
# only starting from kernel 2.6.13
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index 75a75975..2665080e 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -15,6 +15,7 @@ from . import _psutil_osx as cext
from . import _psutil_posix as cext_posix
from ._common import conn_tmap
from ._common import isfile_strict
+from ._common import memoize_when_activated
from ._common import parse_environ_block
from ._common import sockfam_to_enum
from ._common import socktype_to_enum
@@ -56,6 +57,31 @@ PROC_STATUSES = {
cext.SZOMB: _common.STATUS_ZOMBIE,
}
+kinfo_proc_map = dict(
+ ppid=0,
+ ruid=1,
+ euid=2,
+ suid=3,
+ rgid=4,
+ egid=5,
+ sgid=6,
+ ttynr=7,
+ ctime=8,
+ status=9,
+ name=10,
+)
+
+pidtaskinfo_map = dict(
+ cpuutime=0,
+ cpustime=1,
+ rss=2,
+ vms=3,
+ pfaults=4,
+ pageins=5,
+ numthreads=6,
+ volctxsw=7,
+)
+
scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
svmem = namedtuple(
@@ -194,7 +220,9 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext_posix.net_if_duplex_speed(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
@@ -243,6 +271,11 @@ def wrap_exceptions(fun):
try:
return fun(self, *args, **kwargs)
except OSError as err:
+ if self.pid == 0:
+ if 0 in pids():
+ raise AccessDenied(self.pid, self._name)
+ else:
+ raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
@@ -264,15 +297,32 @@ class Process(object):
self._name = None
self._ppid = None
+ @memoize_when_activated
+ def _get_kinfo_proc(self):
+ # Note: should work with all PIDs without permission issues.
+ ret = cext.proc_kinfo_oneshot(self.pid)
+ assert len(ret) == len(kinfo_proc_map)
+ return ret
+
+ @memoize_when_activated
+ def _get_pidtaskinfo(self):
+ # Note: should work for PIDs owned by user only.
+ ret = cext.proc_pidtaskinfo_oneshot(self.pid)
+ assert len(ret) == len(pidtaskinfo_map)
+ return ret
+
def oneshot_enter(self):
- pass
+ self._get_kinfo_proc.cache_activate()
+ self._get_pidtaskinfo.cache_activate()
def oneshot_exit(self):
- pass
+ self._get_kinfo_proc.cache_deactivate()
+ self._get_pidtaskinfo.cache_deactivate()
@wrap_exceptions
def name(self):
- return cext.proc_name(self.pid)
+ name = self._get_kinfo_proc()[kinfo_proc_map['name']]
+ return name if name is not None else cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
@@ -292,7 +342,7 @@ class Process(object):
@wrap_exceptions
def ppid(self):
- self._ppid = cext.proc_ppid(self.pid)
+ self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
return self._ppid
@wrap_exceptions
@@ -301,17 +351,23 @@ class Process(object):
@wrap_exceptions
def uids(self):
- real, effective, saved = cext.proc_uids(self.pid)
- return _common.puids(real, effective, saved)
+ rawtuple = self._get_kinfo_proc()
+ return _common.puids(
+ rawtuple[kinfo_proc_map['ruid']],
+ rawtuple[kinfo_proc_map['euid']],
+ rawtuple[kinfo_proc_map['suid']])
@wrap_exceptions
def gids(self):
- real, effective, saved = cext.proc_gids(self.pid)
- return _common.pgids(real, effective, saved)
+ rawtuple = self._get_kinfo_proc()
+ return _common.puids(
+ rawtuple[kinfo_proc_map['rgid']],
+ rawtuple[kinfo_proc_map['egid']],
+ rawtuple[kinfo_proc_map['sgid']])
@wrap_exceptions
def terminal(self):
- tty_nr = cext.proc_tty_nr(self.pid)
+ tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']]
tmap = _psposix.get_terminal_map()
try:
return tmap[tty_nr]
@@ -320,8 +376,13 @@ class Process(object):
@wrap_exceptions
def memory_info(self):
- rss, vms, pfaults, pageins = cext.proc_memory_info(self.pid)
- return pmem(rss, vms, pfaults, pageins)
+ rawtuple = self._get_pidtaskinfo()
+ return pmem(
+ rawtuple[pidtaskinfo_map['rss']],
+ rawtuple[pidtaskinfo_map['vms']],
+ rawtuple[pidtaskinfo_map['pfaults']],
+ rawtuple[pidtaskinfo_map['pageins']],
+ )
@wrap_exceptions
def memory_full_info(self):
@@ -331,21 +392,28 @@ class Process(object):
@wrap_exceptions
def cpu_times(self):
- user, system = cext.proc_cpu_times(self.pid)
- # Children user/system times are not retrievable (set to 0).
- return _common.pcputimes(user, system, 0, 0)
+ rawtuple = self._get_pidtaskinfo()
+ return _common.pcputimes(
+ rawtuple[pidtaskinfo_map['cpuutime']],
+ rawtuple[pidtaskinfo_map['cpustime']],
+ # children user / system times are not retrievable (set to 0)
+ 0, 0)
@wrap_exceptions
def create_time(self):
- return cext.proc_create_time(self.pid)
+ return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
@wrap_exceptions
def num_ctx_switches(self):
- return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
+ # Unvoluntary value seems not to be available;
+ # getrusage() numbers seems to confirm this theory.
+ # We set it to 0.
+ vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']]
+ return _common.pctxsw(vol, 0)
@wrap_exceptions
def num_threads(self):
- return cext.proc_num_threads(self.pid)
+ return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
@wrap_exceptions
def open_files(self):
@@ -399,7 +467,7 @@ class Process(object):
@wrap_exceptions
def status(self):
- code = cext.proc_status(self.pid)
+ code = self._get_kinfo_proc()[kinfo_proc_map['status']]
# XXX is '?' legit? (we're not supposed to return it anyway)
return PROC_STATUSES.get(code, '?')
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 8eeb6d61..a62e0bf5 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -324,6 +324,11 @@ def wrap_exceptions(fun):
try:
return fun(self, *args, **kwargs)
except EnvironmentError as err:
+ if self.pid == 0:
+ if 0 in pids():
+ raise AccessDenied(self.pid, self._name)
+ else:
+ raise
# ENOENT (no such file or directory) gets raised on open().
# ESRCH (no such process) can get raised on read() if
# process is gone in meantime.
@@ -494,7 +499,7 @@ class Process(object):
return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
except OSError as err:
if err.errno == errno.ENOENT:
- os.stat("%s/%s" % (procfs_path, self.pid))
+ os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
return None
raise
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index d61d5b5a..38659b37 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -129,9 +129,17 @@ psutil_pids(PyObject *self, PyObject *args) {
if (py_retlist == NULL)
return NULL;
+
+ // TODO: RuntimeError is inappropriate here; we could return the
+ // original error instead.
if (psutil_get_proc_list(&proclist, &num_processes) != 0) {
- PyErr_SetString(PyExc_RuntimeError,
- "failed to retrieve process list.");
+ if (errno != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "failed to retrieve process list");
+ }
goto error;
}
@@ -197,12 +205,33 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
long memstack;
kinfo_proc kp;
long pagesize = sysconf(_SC_PAGESIZE);
+ char str[1000];
+ PyObject *py_name;
+ PyObject *py_retlist;
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
if (psutil_kinfo_proc(pid, &kp) == -1)
return NULL;
+ // Process
+#ifdef __FreeBSD__
+ sprintf(str, "%s", kp.ki_comm);
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
+ sprintf(str, "%s", kp.p_comm);
+#endif
+#if PY_MAJOR_VERSION >= 3
+ py_name = PyUnicode_DecodeFSDefault(str);
+#else
+ py_name = Py_BuildValue("s", str);
+#endif
+ if (! py_name) {
+ // Likely a decoding error. We don't want to fail the whole
+ // operation. The python module may retry with proc_name().
+ PyErr_Clear();
+ py_name = Py_None;
+ }
+
// Calculate memory.
#ifdef __FreeBSD__
rss = (long)kp.ki_rssize * pagesize;
@@ -229,8 +258,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
#endif
// Return a single big tuple with all process info.
- return Py_BuildValue(
- "(lillllllidllllddddlllll)",
+ py_retlist = Py_BuildValue(
+ "(lillllllidllllddddlllllO)",
#ifdef __FreeBSD__
//
(long)kp.ki_ppid, // (long) ppid
@@ -262,7 +291,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
vms, // (long) vms
memtext, // (long) mem text
memdata, // (long) mem data
- memstack // (long) mem stack
+ memstack, // (long) mem stack
#elif defined(__OpenBSD__) || defined(__NetBSD__)
//
(long)kp.p_ppid, // (long) ppid
@@ -270,7 +299,7 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
// UIDs
(long)kp.p_ruid, // (long) real uid
(long)kp.p_uid, // (long) effective uid
- (long)kp.p_svuid // (long) saved uid
+ (long)kp.p_svuid, // (long) saved uid
// GIDs
(long)kp.p_rgid, // (long) real gid
(long)kp.p_groups[0], // (long) effective gid
@@ -296,9 +325,16 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
vms, // (long) vms
memtext, // (long) mem text
memdata, // (long) mem data
- memstack // (long) mem stack
+ memstack, // (long) mem stack
#endif
+ py_name // (pystr) name
);
+
+ if (py_retlist != NULL) {
+ // XXX shall we decref() also in case of Py_BuildValue() error?
+ Py_DECREF(py_name);
+ }
+ return py_retlist;
}
@@ -429,9 +465,10 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
if (psutil_kinfo_proc(pid, &kipp) == -1)
goto error;
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
goto error;
}
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index 1c530d4d..5d025739 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -6,6 +6,11 @@
* Routines common to all platforms.
*/
+#ifdef PSUTIL_POSIX
+#include <sys/types.h>
+#include <signal.h>
+#endif
+
#include <Python.h>
@@ -35,3 +40,87 @@ AccessDenied(void) {
Py_XDECREF(exc);
return NULL;
}
+
+
+#ifdef PSUTIL_POSIX
+/*
+ * Check if PID exists. Return values:
+ * 1: exists
+ * 0: does not exist
+ * -1: error (Python exception is set)
+ */
+int
+psutil_pid_exists(long pid) {
+ int ret;
+
+ // No negative PID exists, plus -1 is an alias for sending signal
+ // too all processes except system ones. Not what we want.
+ if (pid < 0)
+ return 0;
+
+ // As per "man 2 kill" PID 0 is an alias for sending the signal to
+ // every process in the process group of the calling process.
+ // Not what we want. Some platforms have PID 0, some do not.
+ // We decide that at runtime.
+ if (pid == 0) {
+#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+ return 0;
+#else
+ return 1;
+#endif
+ }
+
+#if defined(PSUTIL_OSX)
+ ret = kill((pid_t)pid , 0);
+#else
+ ret = kill(pid , 0);
+#endif
+
+ if (ret == 0)
+ return 1;
+ else {
+ if (errno == ESRCH) {
+ // ESRCH == No such process
+ return 0;
+ }
+ else if (errno == EPERM) {
+ // EPERM clearly indicates there's a process to deny
+ // access to.
+ return 1;
+ }
+ else {
+ // According to "man 2 kill" possible error values are
+ // (EINVAL, EPERM, ESRCH) therefore we should never get
+ // here. If we do let's be explicit in considering this
+ // an error.
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ }
+}
+
+
+/*
+ * Utility used for those syscalls which do not return a meaningful
+ * error that we can translate into an exception which makes sense.
+ * As such, we'll have to guess.
+ * On UNIX, if errno is set, we return that one (OSError).
+ * Else, if PID does not exist we assume the syscall failed because
+ * of that so we raise NoSuchProcess.
+ * If none of this is true we giveup and raise RuntimeError(msg).
+ * This will always set a Python exception and return NULL.
+ */
+int
+psutil_raise_for_pid(long pid, char *msg) {
+ // Set exception to AccessDenied if pid exists else NoSuchProcess.
+ if (errno != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 0;
+ }
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess();
+ else
+ PyErr_SetString(PyExc_RuntimeError, msg);
+ return 0;
+}
+#endif
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 43021a72..982c59c7 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -8,3 +8,8 @@
PyObject* AccessDenied(void);
PyObject* NoSuchProcess(void);
+
+#ifdef PSUTIL_POSIX
+int psutil_pid_exists(long pid);
+void psutil_raise_for_pid(long pid, char *msg);
+#endif
diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c
index 19fd14b9..a900919d 100644
--- a/psutil/_psutil_linux.c
+++ b/psutil/_psutil_linux.c
@@ -208,7 +208,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
while ((entry = getmntent(file))) {
if (entry == NULL) {
- PyErr_Format(PyExc_RuntimeError, "getmntent() failed");
+ PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
goto error;
}
py_tuple = Py_BuildValue("(ssss)",
@@ -402,11 +402,15 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
#else
long value = PyInt_AsLong(item);
#endif
- if (value == -1 && PyErr_Occurred())
+ if ((value == -1) || PyErr_Occurred()) {
+ if (!PyErr_Occurred())
+ PyErr_SetString(PyExc_ValueError, "invalid CPU value");
goto error;
+ }
CPU_SET(value, &cpu_set);
}
+
len = sizeof(cpu_set);
if (sched_setaffinity(pid, len, &cpu_set)) {
PyErr_SetFromErrno(PyExc_OSError);
@@ -476,16 +480,14 @@ error:
* http://www.i-scream.org/libstatgrab/
*/
static PyObject*
-psutil_net_if_stats(PyObject* self, PyObject* args) {
+psutil_net_if_duplex_speed(PyObject* self, PyObject* args) {
char *nic_name;
int sock = 0;
int ret;
int duplex;
int speed;
- int mtu;
struct ifreq ifr;
struct ethtool_cmd ethcmd;
- PyObject *py_is_up = NULL;
PyObject *py_retlist = NULL;
if (! PyArg_ParseTuple(args, "s", &nic_name))
@@ -496,22 +498,6 @@ psutil_net_if_stats(PyObject* self, PyObject* args) {
goto error;
strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
- // is up?
- ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
- if (ret == -1)
- goto error;
- if ((ifr.ifr_flags & IFF_UP) != 0)
- py_is_up = Py_True;
- else
- py_is_up = Py_False;
- Py_INCREF(py_is_up);
-
- // MTU
- ret = ioctl(sock, SIOCGIFMTU, &ifr);
- if (ret == -1)
- goto error;
- mtu = ifr.ifr_mtu;
-
// duplex and speed
memset(&ethcmd, 0, sizeof ethcmd);
ethcmd.cmd = ETHTOOL_GSET;
@@ -537,15 +523,13 @@ psutil_net_if_stats(PyObject* self, PyObject* args) {
}
close(sock);
- py_retlist = Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu);
+ py_retlist = Py_BuildValue("[ii]", duplex, speed);
if (!py_retlist)
goto error;
- Py_DECREF(py_is_up);
return py_retlist;
error:
- Py_XDECREF(py_is_up);
- if (sock != 0)
+ if (sock != -1)
close(sock);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
@@ -578,8 +562,8 @@ PsutilMethods[] = {
"device, mount point and filesystem type"},
{"users", psutil_users, METH_VARARGS,
"Return currently connected users as a list of tuples"},
- {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
- "Return NIC stats (isup, duplex, speed, mtu)"},
+ {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
+ "Return duplex and speed info about a NIC"},
// --- linux specific
diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c
index b78035e6..a1168c29 100644
--- a/psutil/_psutil_osx.c
+++ b/psutil/_psutil_osx.c
@@ -57,8 +57,10 @@ psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
if (ret != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError,
- "host_statistics() failed: %s", mach_error_string(ret));
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_VM_INFO) syscall failed: %s",
+ mach_error_string(ret));
return 0;
}
mach_port_deallocate(mach_task_self(), mport);
@@ -67,20 +69,6 @@ psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
/*
- * Set exception to AccessDenied if pid exists else NoSuchProcess.
- */
-void
-psutil_raise_ad_or_nsp(long pid) {
- int ret;
- ret = psutil_pid_exists(pid);
- if (ret == 0)
- NoSuchProcess();
- else
- AccessDenied();
-}
-
-
-/*
* Return a Python list of all the PIDs running on the system.
*/
static PyObject *
@@ -96,8 +84,13 @@ psutil_pids(PyObject *self, PyObject *args) {
return NULL;
if (psutil_get_proc_list(&proclist, &num_processes) != 0) {
- PyErr_SetString(PyExc_RuntimeError,
- "failed to retrieve process list.");
+ if (errno != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ }
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "failed to retrieve process list");
+ }
goto error;
}
@@ -127,6 +120,98 @@ error:
/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using sysctl() and filling up a kinfo_proc struct.
+ * It should be possible to do this for all processes without
+ * incurring into permission (EPERM) errors.
+ */
+static PyObject *
+psutil_proc_kinfo_oneshot(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ PyObject *py_name;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_get_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+
+#if PY_MAJOR_VERSION >= 3
+ py_name = PyUnicode_DecodeFSDefault(kp.kp_proc.p_comm);
+#else
+ py_name = Py_BuildValue("s", kp.kp_proc.p_comm);
+#endif
+ if (! py_name) {
+ // Likely a decoding error. We don't want to fail the whole
+ // operation. The python module may retry with proc_name().
+ PyErr_Clear();
+ py_name = Py_None;
+ }
+
+ py_retlist = Py_BuildValue(
+ "lllllllidiO",
+ (long)kp.kp_eproc.e_ppid, // (long) ppid
+ (long)kp.kp_eproc.e_pcred.p_ruid, // (long) real uid
+ (long)kp.kp_eproc.e_ucred.cr_uid, // (long) effective uid
+ (long)kp.kp_eproc.e_pcred.p_svuid, // (long) saved uid
+ (long)kp.kp_eproc.e_pcred.p_rgid, // (long) real gid
+ (long)kp.kp_eproc.e_ucred.cr_groups[0], // (long) effective gid
+ (long)kp.kp_eproc.e_pcred.p_svgid, // (long) saved gid
+ kp.kp_eproc.e_tdev, // (int) tty nr
+ PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime), // (double) create time
+ (int)kp.kp_proc.p_stat, // (int) status
+ py_name // (pystr) name
+ );
+
+ if (py_retlist != NULL) {
+ // XXX shall we decref() also in case of Py_BuildValue() error?
+ Py_DECREF(py_name);
+ }
+ return py_retlist;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using proc_pidinfo(PROC_PIDTASKINFO) and filling a proc_taskinfo
+ * struct.
+ * Contrarily from proc_kinfo above this function will return EACCES
+ * for PIDs owned by another user.
+ */
+static PyObject *
+psutil_proc_pidtaskinfo_oneshot(PyObject *self, PyObject *args) {
+ long pid;
+ struct proc_taskinfo pti;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti)) <= 0)
+ return NULL;
+
+ return Py_BuildValue(
+ "(ddKKkkkk)",
+ (float)pti.pti_total_user / 1000000000.0, // (float) cpu user time
+ (float)pti.pti_total_system / 1000000000.0, // (float) cpu sys time
+ // Note about memory: determining other mem stats on OSX is a mess:
+ // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
+ // I just give up.
+ // struct proc_regioninfo pri;
+ // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, 0, &pri, sizeof(pri))
+ pti.pti_resident_size, // (uns long long) rss
+ pti.pti_virtual_size, // (uns long long) vms
+ pti.pti_faults, // (uns long) number of page faults (pages)
+ pti.pti_pageins, // (uns long) number of actual pageins (pages)
+ pti.pti_threadnum, // (uns long) num threads
+ // Unvoluntary value seems not to be available;
+ // pti.pti_csw probably refers to the sum of the two;
+ // getrusage() numbers seems to confirm this theory.
+ pti.pti_csw // (uns long) voluntary ctx switches
+ );
+}
+
+
+/*
* Return process name from kinfo_proc as a Python string.
*/
static PyObject *
@@ -143,7 +228,6 @@ psutil_proc_name(PyObject *self, PyObject *args) {
#else
return Py_BuildValue("s", kp.kp_proc.p_comm);
#endif
-
}
@@ -158,8 +242,8 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
- if (! psutil_proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, &pathinfo,
- sizeof(pathinfo)))
+ if (psutil_proc_pidinfo(
+ pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)) <= 0)
{
return NULL;
}
@@ -183,9 +267,13 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
- ret = proc_pidpath(pid, &buf, sizeof(buf));
+ errno = 0;
+ ret = proc_pidpath((pid_t)pid, &buf, sizeof(buf));
if (ret == 0) {
- psutil_raise_ad_or_nsp(pid);
+ if (pid == 0)
+ AccessDenied();
+ else
+ psutil_raise_for_pid(pid, "proc_pidpath() syscall failed");
return NULL;
}
#if PY_MAJOR_VERSION >= 3
@@ -231,72 +319,6 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
/*
- * Return process parent pid from kinfo_proc as a Python integer.
- */
-static PyObject *
-psutil_proc_ppid(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid);
-}
-
-
-/*
- * Return process real uid from kinfo_proc as a Python integer.
- */
-static PyObject *
-psutil_proc_uids(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("lll",
- (long)kp.kp_eproc.e_pcred.p_ruid,
- (long)kp.kp_eproc.e_ucred.cr_uid,
- (long)kp.kp_eproc.e_pcred.p_svuid);
-}
-
-
-/*
- * Return process real group id from ki_comm as a Python integer.
- */
-static PyObject *
-psutil_proc_gids(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("lll",
- (long)kp.kp_eproc.e_pcred.p_rgid,
- (long)kp.kp_eproc.e_ucred.cr_groups[0],
- (long)kp.kp_eproc.e_pcred.p_svgid);
-}
-
-
-/*
- * Return process controlling terminal number as an integer.
- */
-static PyObject *
-psutil_proc_tty_nr(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("i", kp.kp_eproc.e_tdev);
-}
-
-
-/*
* Return a list of tuples for every process memory maps.
* 'procstat' cmdline utility has been used as an example.
*/
@@ -322,10 +344,12 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
goto error;
- err = task_for_pid(mach_task_self(), pid, &task);
-
+ err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
- psutil_raise_ad_or_nsp(pid);
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess();
+ else
+ AccessDenied();
goto error;
}
@@ -347,7 +371,10 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
memset(addr_str, 0, sizeof(addr_str));
memset(perms, 0, sizeof(perms));
- sprintf(addr_str, "%016lx-%016lx", address, address + size);
+ sprintf(addr_str,
+ "%016lx-%016lx",
+ (long unsigned int)address,
+ (long unsigned int)address + size);
sprintf(perms, "%c%c%c/%c%c%c",
(info.protection & VM_PROT_READ) ? 'r' : '-',
(info.protection & VM_PROT_WRITE) ? 'w' : '-',
@@ -356,7 +383,16 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
(info.max_protection & VM_PROT_WRITE) ? 'w' : '-',
(info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-');
- err = proc_regionfilename(pid, address, buf, sizeof(buf));
+ // proc_regionfilename() return value seems meaningless
+ // so we do what we can in order to not continue in case
+ // of error.
+ errno = 0;
+ proc_regionfilename((pid_t)pid, address, buf, sizeof(buf));
+ if ((errno != 0) || ((sizeof(buf)) <= 0)) {
+ psutil_raise_for_pid(
+ pid, "proc_regionfilename() syscall failed");
+ goto error;
+ }
if (info.share_mode == SM_COW && info.ref_count == 1) {
// Treat single reference SM_COW as SM_PRIVATE
@@ -476,67 +512,6 @@ psutil_cpu_count_phys(PyObject *self, PyObject *args) {
/*
- * Return a Python tuple (user_time, kernel_time)
- */
-static PyObject *
-psutil_proc_cpu_times(PyObject *self, PyObject *args) {
- long pid;
- struct proc_taskinfo pti;
-
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
- return NULL;
- return Py_BuildValue("(dd)",
- (float)pti.pti_total_user / 1000000000.0,
- (float)pti.pti_total_system / 1000000000.0);
-}
-
-
-/*
- * Return a Python float indicating the process create time expressed in
- * seconds since the epoch.
- */
-static PyObject *
-psutil_proc_create_time(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("d", PSUTIL_TV2DOUBLE(kp.kp_proc.p_starttime));
-}
-
-
-/*
- * Return extended memory info about a process.
- */
-static PyObject *
-psutil_proc_memory_info(PyObject *self, PyObject *args) {
- long pid;
- struct proc_taskinfo pti;
-
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
- return NULL;
- // Note: determining other memory stats on OSX is a mess:
- // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt
- // I just give up...
- // struct proc_regioninfo pri;
- // psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, &pri, sizeof(pri))
- return Py_BuildValue(
- "(KKkk)",
- pti.pti_resident_size, // resident memory size (rss)
- pti.pti_virtual_size, // virtual memory size (vms)
- pti.pti_faults, // number of page faults (pages)
- pti.pti_pageins // number of actual pageins (pages)
- );
-}
-
-
-/*
* Indicates if the given virtual address on the given architecture is in the
* shared VM region.
*/
@@ -590,9 +565,12 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
- err = task_for_pid(mach_task_self(), pid, &task);
+ err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
- psutil_raise_ad_or_nsp(pid);
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess();
+ else
+ AccessDenied();
return NULL;
}
@@ -613,7 +591,9 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
break;
}
else if (kr != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError, "mach_vm_region() failed");
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "mach_vm_region(VM_REGION_TOP_INFO) syscall failed");
return NULL;
}
@@ -655,42 +635,10 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
/*
- * Return number of threads used by process as a Python integer.
- */
-static PyObject *
-psutil_proc_num_threads(PyObject *self, PyObject *args) {
- long pid;
- struct proc_taskinfo pti;
-
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
- return NULL;
- return Py_BuildValue("k", pti.pti_threadnum);
-}
-
-
-/*
- * Return the number of context switches performed by process.
- */
-static PyObject *
-psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
- long pid;
- struct proc_taskinfo pti;
-
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti)))
- return NULL;
- // unvoluntary value seems not to be available;
- // pti.pti_csw probably refers to the sum of the two (getrusage()
- // numbers seems to confirm this theory).
- return Py_BuildValue("ki", pti.pti_csw, 0);
-}
-
-
-/*
- * Return system virtual memory stats
+ * Return system virtual memory stats.
+ * See:
+ * http://opensource.apple.com/source/system_cmds/system_cmds-498.2/
+ * vm_stat.tproj/vm_stat.c
*/
static PyObject *
psutil_virtual_mem(PyObject *self, PyObject *args) {
@@ -708,7 +656,8 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
if (errno != 0)
PyErr_SetFromErrno(PyExc_OSError);
else
- PyErr_Format(PyExc_RuntimeError, "sysctl(HW_MEMSIZE) failed");
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
return NULL;
}
@@ -722,7 +671,8 @@ psutil_virtual_mem(PyObject *self, PyObject *args) {
(unsigned long long) vm.active_count * pagesize,
(unsigned long long) vm.inactive_count * pagesize,
(unsigned long long) vm.wire_count * pagesize,
- (unsigned long long) vm.free_count * pagesize
+ // this is how vm_stat cmd does it
+ (unsigned long long) (vm.free_count - vm.speculative_count) * pagesize
);
}
@@ -745,7 +695,8 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
if (errno != 0)
PyErr_SetFromErrno(PyExc_OSError);
else
- PyErr_Format(PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) failed");
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
return NULL;
}
if (!psutil_sys_vminfo(&vmstat))
@@ -773,10 +724,12 @@ psutil_cpu_times(PyObject *self, PyObject *args) {
mach_port_t host_port = mach_host_self();
error = host_statistics(host_port, HOST_CPU_LOAD_INFO,
(host_info_t)&r_load, &count);
- if (error != KERN_SUCCESS)
- return PyErr_Format(PyExc_RuntimeError,
- "Error in host_statistics(): %s",
- mach_error_string(error));
+ if (error != KERN_SUCCESS) {
+ return PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: %s",
+ mach_error_string(error));
+ }
mach_port_deallocate(mach_task_self(), host_port);
return Py_BuildValue(
@@ -795,11 +748,12 @@ psutil_cpu_times(PyObject *self, PyObject *args) {
static PyObject *
psutil_per_cpu_times(PyObject *self, PyObject *args) {
natural_t cpu_count;
+ natural_t i;
processor_info_array_t info_array;
mach_msg_type_number_t info_count;
kern_return_t error;
processor_cpu_load_info_data_t *cpu_load_info = NULL;
- int i, ret;
+ int ret;
PyObject *py_retlist = PyList_New(0);
PyObject *py_cputime = NULL;
@@ -810,8 +764,10 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) {
error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
&cpu_count, &info_array, &info_count);
if (error != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError, "Error in host_processor_info(): %s",
- mach_error_string(error));
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
+ mach_error_string(error));
goto error;
}
mach_port_deallocate(mach_task_self(), host_port);
@@ -881,7 +837,7 @@ static PyObject *
psutil_disk_partitions(PyObject *self, PyObject *args) {
int num;
int i;
- long len;
+ int len;
uint64_t flags;
char opts[400];
struct statfs *fs = NULL;
@@ -996,27 +952,12 @@ error:
/*
- * Return process status as a Python integer.
- */
-static PyObject *
-psutil_proc_status(PyObject *self, PyObject *args) {
- long pid;
- struct kinfo_proc kp;
- if (! PyArg_ParseTuple(args, "l", &pid))
- return NULL;
- if (psutil_get_kinfo_proc(pid, &kp) == -1)
- return NULL;
- return Py_BuildValue("i", (int)kp.kp_proc.p_stat);
-}
-
-
-/*
* Return process threads
*/
static PyObject *
psutil_proc_threads(PyObject *self, PyObject *args) {
long pid;
- int err, j, ret;
+ int err, ret;
kern_return_t kr;
unsigned int info_count = TASK_BASIC_INFO_COUNT;
mach_port_t task = MACH_PORT_NULL;
@@ -1024,7 +965,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
thread_act_port_array_t thread_list = NULL;
thread_info_data_t thinfo_basic;
thread_basic_info_t basic_info_th;
- mach_msg_type_number_t thread_count, thread_info_count;
+ mach_msg_type_number_t thread_count, thread_info_count, j;
PyObject *py_tuple = NULL;
PyObject *py_retlist = PyList_New(0);
@@ -1037,9 +978,12 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
// task_for_pid() requires special privileges
- err = task_for_pid(mach_task_self(), pid, &task);
+ err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
- psutil_raise_ad_or_nsp(pid);
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess();
+ else
+ AccessDenied();
goto error;
}
@@ -1054,14 +998,14 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
else {
// otherwise throw a runtime error with appropriate error code
PyErr_Format(PyExc_RuntimeError,
- "task_info(TASK_BASIC_INFO) failed");
+ "task_info(TASK_BASIC_INFO) syscall failed");
}
goto error;
}
err = task_threads(task, &thread_list, &thread_count);
if (err != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError, "task_threads() failed");
+ PyErr_Format(PyExc_RuntimeError, "task_threads() syscall failed");
goto error;
}
@@ -1072,7 +1016,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
(thread_info_t)thinfo_basic, &thread_info_count);
if (kr != KERN_SUCCESS) {
PyErr_Format(PyExc_RuntimeError,
- "thread_info() with flag THREAD_BASIC_INFO failed");
+ "thread_info(THREAD_BASIC_INFO) syscall failed");
goto error;
}
@@ -1126,7 +1070,7 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
int pidinfo_result;
int iterations;
int i;
- int nb;
+ unsigned long nb;
struct proc_fdinfo *fds_pointer = NULL;
struct proc_fdinfo *fdp_pointer;
@@ -1142,27 +1086,19 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
goto error;
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
- if (pidinfo_result <= 0) {
- // may be be ignored later if errno != 0
- PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDLISTFDS) failed");
+ pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ if (pidinfo_result <= 0)
goto error;
- }
fds_pointer = malloc(pidinfo_result);
if (fds_pointer == NULL) {
PyErr_NoMemory();
goto error;
}
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
- pidinfo_result);
- if (pidinfo_result <= 0) {
- // may be be ignored later if errno != 0
- PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDLISTFDS) failed");
+ pidinfo_result = psutil_proc_pidinfo(
+ pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
+ if (pidinfo_result <= 0)
goto error;
- }
iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
@@ -1170,31 +1106,25 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
py_tuple = NULL;
fdp_pointer = &fds_pointer[i];
- if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE)
- {
- nb = proc_pidfdinfo(pid,
+ if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
+ errno = 0;
+ nb = proc_pidfdinfo((pid_t)pid,
fdp_pointer->proc_fd,
PROC_PIDFDVNODEPATHINFO,
&vi,
sizeof(vi));
// --- errors checking
- if (nb <= 0) {
+ if ((nb <= 0) || nb < sizeof(vi)) {
if ((errno == ENOENT) || (errno == EBADF)) {
// no such file or directory or bad file descriptor;
// let's assume the file has been closed or removed
continue;
}
- // may be be ignored later if errno != 0
- PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
- goto error;
- }
- if (nb < sizeof(vi)) {
- PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed "
- "(buffer mismatch)");
- goto error;
+ else {
+ psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ goto error;
+ }
}
// --- /errors checking
@@ -1229,12 +1159,7 @@ error:
Py_DECREF(py_retlist);
if (fds_pointer != NULL)
free(fds_pointer);
- if (errno != 0)
- return PyErr_SetFromErrno(PyExc_OSError);
- else if (! psutil_pid_exists(pid))
- return NoSuchProcess();
- else
- return NULL; // exception has already been set earlier
+ return NULL; // exception has already been set earlier
}
@@ -1253,7 +1178,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
int pidinfo_result;
int iterations;
int i;
- int nb;
+ unsigned long nb;
struct proc_fdinfo *fds_pointer = NULL;
struct proc_fdinfo *fdp_pointer;
@@ -1279,7 +1204,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
if (pid == 0)
return py_retlist;
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ pidinfo_result = psutil_proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
if (pidinfo_result <= 0)
goto error;
@@ -1288,44 +1213,34 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
PyErr_NoMemory();
goto error;
}
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
- pidinfo_result);
+ pidinfo_result = psutil_proc_pidinfo(
+ pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result);
if (pidinfo_result <= 0)
goto error;
- iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
+ iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
for (i = 0; i < iterations; i++) {
py_tuple = NULL;
py_laddr = NULL;
py_raddr = NULL;
- errno = 0;
fdp_pointer = &fds_pointer[i];
- if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET)
- {
- nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd,
+ if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) {
+ errno = 0;
+ nb = proc_pidfdinfo((pid_t)pid, fdp_pointer->proc_fd,
PROC_PIDFDSOCKETINFO, &si, sizeof(si));
// --- errors checking
- if (nb <= 0) {
+ if ((nb <= 0) || (nb < sizeof(si))) {
if (errno == EBADF) {
// let's assume socket has been closed
continue;
}
- if (errno != 0)
- PyErr_SetFromErrno(PyExc_OSError);
- else
- PyErr_Format(
- PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed");
- goto error;
- }
- if (nb < sizeof(si)) {
- PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed "
- "(buffer mismatch)");
- goto error;
+ else {
+ psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ goto error;
+ }
}
// --- /errors checking
@@ -1438,16 +1353,9 @@ error:
Py_XDECREF(py_laddr);
Py_XDECREF(py_raddr);
Py_DECREF(py_retlist);
-
if (fds_pointer != NULL)
free(fds_pointer);
- if (errno != 0)
- return PyErr_SetFromErrno(PyExc_OSError);
- else if (! psutil_pid_exists(pid))
- return NoSuchProcess();
- else
- return PyErr_Format(PyExc_RuntimeError,
- "proc_pidinfo(PROC_PIDLISTFDS) failed");
+ return NULL;
}
@@ -1464,14 +1372,14 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+ pidinfo_result = proc_pidinfo((pid_t)pid, PROC_PIDLISTFDS, 0, NULL, 0);
if (pidinfo_result <= 0)
return PyErr_SetFromErrno(PyExc_OSError);
fds_pointer = malloc(pidinfo_result);
if (fds_pointer == NULL)
return PyErr_NoMemory();
- pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer,
+ pidinfo_result = proc_pidinfo((pid_t)pid, PROC_PIDLISTFDS, 0, fds_pointer,
pidinfo_result);
if (pidinfo_result <= 0) {
free(fds_pointer);
@@ -1592,8 +1500,8 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching(kIOMediaClass),
&disk_list) != kIOReturnSuccess) {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the list of disks.");
+ PyErr_SetString(
+ PyExc_RuntimeError, "unable to get the list of disks.");
goto error;
}
@@ -1799,8 +1707,10 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);
if (ret != KERN_SUCCESS) {
- PyErr_Format(PyExc_RuntimeError,
- "host_statistics() failed: %s", mach_error_string(ret));
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_VM_INFO) failed: %s",
+ mach_error_string(ret));
return NULL;
}
mach_port_deallocate(mach_task_self(), mport);
@@ -1825,6 +1735,10 @@ PsutilMethods[] = {
// --- per-process functions
+ {"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS,
+ "Return multiple process info."},
+ {"proc_pidtaskinfo_oneshot", psutil_proc_pidtaskinfo_oneshot, METH_VARARGS,
+ "Return multiple process info."},
{"proc_name", psutil_proc_name, METH_VARARGS,
"Return process name"},
{"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
@@ -1835,37 +1749,16 @@ PsutilMethods[] = {
"Return path of the process executable"},
{"proc_cwd", psutil_proc_cwd, METH_VARARGS,
"Return process current working directory."},
- {"proc_ppid", psutil_proc_ppid, METH_VARARGS,
- "Return process ppid as an integer"},
- {"proc_uids", psutil_proc_uids, METH_VARARGS,
- "Return process real user id as an integer"},
- {"proc_gids", psutil_proc_gids, METH_VARARGS,
- "Return process real group id as an integer"},
- {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
- "Return tuple of user/kern time for the given PID"},
- {"proc_create_time", psutil_proc_create_time, METH_VARARGS,
- "Return a float indicating the process create time expressed in "
- "seconds since the epoch"},
- {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS,
- "Return memory information about a process"},
{"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
"Return process USS memory"},
- {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS,
- "Return number of threads used by process"},
- {"proc_status", psutil_proc_status, METH_VARARGS,
- "Return process status as an integer"},
{"proc_threads", psutil_proc_threads, METH_VARARGS,
"Return process threads as a list of tuples"},
{"proc_open_files", psutil_proc_open_files, METH_VARARGS,
"Return files opened by process as a list of tuples"},
{"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
"Return the number of fds opened by process."},
- {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
- "Return the number of context switches performed by process"},
{"proc_connections", psutil_proc_connections, METH_VARARGS,
"Get process TCP and UDP connections as a list of tuples"},
- {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS,
- "Return process tty number as an integer"},
{"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
"Return a list of tuples for every process's memory map"},
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index 075d51ef..afe4fdac 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -36,8 +36,6 @@
#include <netdb.h>
#endif
-#include "_psutil_posix.h"
-
/*
* Given a PID return process priority as a Python integer.
@@ -247,8 +245,81 @@ error:
/*
- * net_if_stats() implementation. This is here because it is common
- * to both OSX and FreeBSD and I didn't know where else to put it.
+ * Return NIC MTU. References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_mtu(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ int mtu;
+ struct ifreq ifr;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+
+ strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+ ret = ioctl(sock, SIOCGIFMTU, &ifr);
+ if (ret == -1)
+ goto error;
+ close(sock);
+ mtu = ifr.ifr_mtu;
+
+ return Py_BuildValue("i", mtu);
+
+error:
+ if (sock != 0)
+ close(sock);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
+/*
+ * Inspect NIC flags, returns a bool indicating whether the NIC is
+ * running. References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_flags(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ struct ifreq ifr;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+
+ strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+ ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+ if (ret == -1)
+ goto error;
+
+ close(sock);
+ if ((ifr.ifr_flags & IFF_UP) != 0)
+ return Py_BuildValue("O", Py_True);
+ else
+ return Py_BuildValue("O", Py_False);
+
+error:
+ if (sock != 0)
+ close(sock);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
+/*
+ * net_if_stats() OSX/BSD implementation.
*/
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__)
@@ -398,18 +469,15 @@ int psutil_get_nic_speed(int ifm_active) {
* http://www.i-scream.org/libstatgrab/
*/
static PyObject *
-psutil_net_if_stats(PyObject *self, PyObject *args) {
+psutil_net_if_duplex_speed(PyObject *self, PyObject *args) {
char *nic_name;
int sock = 0;
int ret;
int duplex;
int speed;
- int mtu;
struct ifreq ifr;
struct ifmediareq ifmed;
- PyObject *py_is_up = NULL;
-
if (! PyArg_ParseTuple(args, "s", &nic_name))
return NULL;
@@ -418,22 +486,6 @@ psutil_net_if_stats(PyObject *self, PyObject *args) {
goto error;
strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
- // is up?
- ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
- if (ret == -1)
- goto error;
- if ((ifr.ifr_flags & IFF_UP) != 0)
- py_is_up = Py_True;
- else
- py_is_up = Py_False;
- Py_INCREF(py_is_up);
-
- // MTU
- ret = ioctl(sock, SIOCGIFMTU, &ifr);
- if (ret == -1)
- goto error;
- mtu = ifr.ifr_mtu;
-
// speed / duplex
memset(&ifmed, 0, sizeof(struct ifmediareq));
strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name));
@@ -453,18 +505,15 @@ psutil_net_if_stats(PyObject *self, PyObject *args) {
}
close(sock);
- Py_DECREF(py_is_up);
-
- return Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu);
+ return Py_BuildValue("[ii]", duplex, speed);
error:
- Py_XDECREF(py_is_up);
if (sock != 0)
close(sock);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
-#endif // net_if_stats() implementation
+#endif // net_if_stats() OSX/BSD implementation
/*
@@ -478,8 +527,12 @@ PsutilMethods[] = {
"Set process priority"},
{"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
"Retrieve NICs information"},
+ {"net_if_mtu", psutil_net_if_mtu, METH_VARARGS,
+ "Retrieve NIC MTU"},
+ {"net_if_flags", psutil_net_if_flags, METH_VARARGS,
+ "Retrieve NIC flags"},
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__)
- {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+ {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
"Return NIC stats."},
#endif
{NULL, NULL, 0, NULL}
diff --git a/psutil/_psutil_posix.h b/psutil/_psutil_posix.h
index bbe6fc5a..86708f4b 100644
--- a/psutil/_psutil_posix.h
+++ b/psutil/_psutil_posix.h
@@ -3,13 +3,3 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-
-#include <Python.h>
-
-static PyObject* psutil_net_if_addrs(PyObject* self, PyObject* args);
-static PyObject* psutil_posix_getpriority(PyObject* self, PyObject* args);
-static PyObject* psutil_posix_setpriority(PyObject* self, PyObject* args);
-
-#if defined(__FreeBSD__) || defined(__APPLE__)
-static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
-#endif
diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c
index 0ceec54c..e98ff7f2 100644
--- a/psutil/_psutil_sunos.c
+++ b/psutil/_psutil_sunos.c
@@ -71,7 +71,8 @@ psutil_file_to_struct(char *path, void *fstruct, size_t size) {
}
if (nbytes != size) {
close(fd);
- PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
+ PyErr_SetString(
+ PyExc_RuntimeError, "read() file structure size mismatch");
return 0;
}
close(fd);
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index ef0af7ec..7464bbf9 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -174,7 +174,8 @@ psutil_get_nic_addresses() {
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3));
if (dwRetVal != NO_ERROR) {
- PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed.");
+ PyErr_SetString(
+ PyExc_RuntimeError, "GetAdaptersAddresses() syscall failed.");
return NULL;
}
@@ -1457,6 +1458,88 @@ psutil_proc_username(PyObject *self, PyObject *args) {
}
+typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG,
+ TCP_TABLE_CLASS, ULONG);
+
+
+// https://msdn.microsoft.com/library/aa365928.aspx
+static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call,
+ ULONG address_family,
+ PVOID * data, DWORD * size)
+{
+ // Due to other processes being active on the machine, it's possible
+ // that the size of the table increases between the moment where we
+ // query the size and the moment where we query the data. Therefore, it's
+ // important to call this in a loop to retry if that happens.
+ //
+ // Also, since we may loop a theoretically unbounded number of times here,
+ // release the GIL while we're doing this.
+ DWORD error = ERROR_INSUFFICIENT_BUFFER;
+ *size = 0;
+ *data = NULL;
+ Py_BEGIN_ALLOW_THREADS;
+ error = call(NULL, size, FALSE, address_family,
+ TCP_TABLE_OWNER_PID_ALL, 0);
+ while (error == ERROR_INSUFFICIENT_BUFFER)
+ {
+ *data = malloc(*size);
+ if (*data == NULL) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ continue;
+ }
+ error = call(*data, size, FALSE, address_family,
+ TCP_TABLE_OWNER_PID_ALL, 0);
+ if (error != NO_ERROR) {
+ free(*data);
+ *data = NULL;
+ }
+ }
+ Py_END_ALLOW_THREADS;
+ return error;
+}
+
+
+typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG,
+ UDP_TABLE_CLASS, ULONG);
+
+
+// https://msdn.microsoft.com/library/aa365930.aspx
+static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call,
+ ULONG address_family,
+ PVOID * data, DWORD * size)
+{
+ // Due to other processes being active on the machine, it's possible
+ // that the size of the table increases between the moment where we
+ // query the size and the moment where we query the data. Therefore, it's
+ // important to call this in a loop to retry if that happens.
+ //
+ // Also, since we may loop a theoretically unbounded number of times here,
+ // release the GIL while we're doing this.
+ DWORD error = ERROR_INSUFFICIENT_BUFFER;
+ *size = 0;
+ *data = NULL;
+ Py_BEGIN_ALLOW_THREADS;
+ error = call(NULL, size, FALSE, address_family,
+ UDP_TABLE_OWNER_PID, 0);
+ while (error == ERROR_INSUFFICIENT_BUFFER)
+ {
+ *data = malloc(*size);
+ if (*data == NULL) {
+ error = ERROR_NOT_ENOUGH_MEMORY;
+ continue;
+ }
+ error = call(*data, size, FALSE, address_family,
+ UDP_TABLE_OWNER_PID, 0);
+ if (error != NO_ERROR) {
+ free(*data);
+ *data = NULL;
+ }
+ }
+ Py_END_ALLOW_THREADS;
+ return error;
+}
+
+
/*
* Return a list of network connections opened by a process
*/
@@ -1468,14 +1551,11 @@ psutil_net_connections(PyObject *self, PyObject *args) {
_RtlIpv4AddressToStringA rtlIpv4AddressToStringA;
typedef PSTR (NTAPI * _RtlIpv6AddressToStringA)(struct in6_addr *, PSTR);
_RtlIpv6AddressToStringA rtlIpv6AddressToStringA;
- typedef DWORD (WINAPI * _GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG,
- TCP_TABLE_CLASS, ULONG);
_GetExtendedTcpTable getExtendedTcpTable;
- typedef DWORD (WINAPI * _GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG,
- UDP_TABLE_CLASS, ULONG);
_GetExtendedUdpTable getExtendedUdpTable;
PVOID table = NULL;
DWORD tableSize;
+ DWORD error;
PMIB_TCPTABLE_OWNER_PID tcp4Table;
PMIB_UDPTABLE_OWNER_PID udp4Table;
PMIB_TCP6TABLE_OWNER_PID tcp6Table;
@@ -1558,17 +1638,15 @@ psutil_net_connections(PyObject *self, PyObject *args) {
py_addr_tuple_local = NULL;
py_addr_tuple_remote = NULL;
tableSize = 0;
- getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET,
- TCP_TABLE_OWNER_PID_ALL, 0);
- table = malloc(tableSize);
- if (table == NULL) {
+ error = __GetExtendedTcpTable(getExtendedTcpTable,
+ AF_INET, &table, &tableSize);
+ if (error == ERROR_NOT_ENOUGH_MEMORY) {
PyErr_NoMemory();
goto error;
}
- if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET,
- TCP_TABLE_OWNER_PID_ALL, 0) == 0)
+ if (error == NO_ERROR)
{
tcp4Table = table;
@@ -1638,8 +1716,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
Py_DECREF(py_conn_tuple);
}
}
+ else {
+ PyErr_SetFromWindowsErr(error);
+ goto error;
+ }
free(table);
+ table = NULL;
+ tableSize = 0;
}
// TCP IPv6
@@ -1651,17 +1735,15 @@ psutil_net_connections(PyObject *self, PyObject *args) {
py_addr_tuple_local = NULL;
py_addr_tuple_remote = NULL;
tableSize = 0;
- getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6,
- TCP_TABLE_OWNER_PID_ALL, 0);
- table = malloc(tableSize);
- if (table == NULL) {
+ error = __GetExtendedTcpTable(getExtendedTcpTable,
+ AF_INET6, &table, &tableSize);
+ if (error == ERROR_NOT_ENOUGH_MEMORY) {
PyErr_NoMemory();
goto error;
}
- if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET6,
- TCP_TABLE_OWNER_PID_ALL, 0) == 0)
+ if (error == NO_ERROR)
{
tcp6Table = table;
@@ -1731,8 +1813,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
Py_DECREF(py_conn_tuple);
}
}
+ else {
+ PyErr_SetFromWindowsErr(error);
+ goto error;
+ }
free(table);
+ table = NULL;
+ tableSize = 0;
}
// UDP IPv4
@@ -1745,17 +1833,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
py_addr_tuple_local = NULL;
py_addr_tuple_remote = NULL;
tableSize = 0;
- getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET,
- UDP_TABLE_OWNER_PID, 0);
-
- table = malloc(tableSize);
- if (table == NULL) {
+ error = __GetExtendedUdpTable(getExtendedUdpTable,
+ AF_INET, &table, &tableSize);
+ if (error == ERROR_NOT_ENOUGH_MEMORY) {
PyErr_NoMemory();
goto error;
}
- if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET,
- UDP_TABLE_OWNER_PID, 0) == 0)
+ if (error == NO_ERROR)
{
udp4Table = table;
@@ -1802,8 +1887,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
Py_DECREF(py_conn_tuple);
}
}
+ else {
+ PyErr_SetFromWindowsErr(error);
+ goto error;
+ }
free(table);
+ table = NULL;
+ tableSize = 0;
}
// UDP IPv6
@@ -1816,17 +1907,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
py_addr_tuple_local = NULL;
py_addr_tuple_remote = NULL;
tableSize = 0;
- getExtendedUdpTable(NULL, &tableSize, FALSE,
- AF_INET6, UDP_TABLE_OWNER_PID, 0);
-
- table = malloc(tableSize);
- if (table == NULL) {
+ error = __GetExtendedUdpTable(getExtendedUdpTable,
+ AF_INET6, &table, &tableSize);
+ if (error == ERROR_NOT_ENOUGH_MEMORY) {
PyErr_NoMemory();
goto error;
}
- if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET6,
- UDP_TABLE_OWNER_PID, 0) == 0)
+ if (error == NO_ERROR)
{
udp6Table = table;
@@ -1872,8 +1960,14 @@ psutil_net_connections(PyObject *self, PyObject *args) {
Py_DECREF(py_conn_tuple);
}
}
+ else {
+ PyErr_SetFromWindowsErr(error);
+ goto error;
+ }
free(table);
+ table = NULL;
+ tableSize = 0;
}
_psutil_conn_decref_objs();
@@ -1980,7 +2074,7 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
if (NtSetInformationProcess == NULL) {
PyErr_SetString(PyExc_RuntimeError,
- "couldn't get NtSetInformationProcess");
+ "couldn't get NtSetInformationProcess syscall");
return NULL;
}
@@ -2201,7 +2295,7 @@ psutil_net_io_counters(PyObject *self, PyObject *args) {
if (dwRetVal != NO_ERROR) {
PyErr_SetString(PyExc_RuntimeError,
- "GetIfEntry() or GetIfEntry2() failed.");
+ "GetIfEntry() or GetIfEntry2() syscalls failed.");
goto error;
}
@@ -2875,9 +2969,18 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
unsigned int i = 0;
ULONG family;
PCTSTR intRet;
+ PCTSTR netmaskIntRet;
char *ptr;
char buff[100];
DWORD bufflen = 100;
+ char netmask_buff[100];
+ DWORD netmask_bufflen = 100;
+ DWORD dwRetVal = 0;
+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above
+ ULONG converted_netmask;
+ UINT netmask_bits;
+ struct in_addr in_netmask;
+#endif
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
@@ -2887,6 +2990,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
PyObject *py_address = NULL;
PyObject *py_mac_address = NULL;
PyObject *py_nic_name = NULL;
+ PyObject *py_netmask = NULL;
if (py_retlist == NULL)
return NULL;
@@ -2899,6 +3003,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
while (pCurrAddresses) {
pUnicast = pCurrAddresses->FirstUnicastAddress;
+ netmaskIntRet = NULL;
py_nic_name = NULL;
py_nic_name = PyUnicode_FromWideChar(
pCurrAddresses->FriendlyName,
@@ -2960,6 +3065,15 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
pUnicast->Address.lpSockaddr;
intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff,
bufflen);
+#if (_WIN32_WINNT >= 0x0600) // Windows Vista and above
+ netmask_bits = pUnicast->OnLinkPrefixLength;
+ dwRetVal = ConvertLengthToIpv4Mask(netmask_bits, &converted_netmask);
+ if (dwRetVal == NO_ERROR) {
+ in_netmask.s_addr = converted_netmask;
+ netmaskIntRet = inet_ntop(AF_INET, &in_netmask, netmask_buff,
+ netmask_bufflen);
+ }
+#endif
}
else if (family == AF_INET6) {
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
@@ -2985,7 +3099,17 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
if (py_address == NULL)
goto error;
- Py_INCREF(Py_None);
+ if (netmaskIntRet != NULL) {
+#if PY_MAJOR_VERSION >= 3
+ py_netmask = PyUnicode_FromString(netmask_buff);
+#else
+ py_netmask = PyString_FromString(netmask_buff);
+#endif
+ } else {
+ Py_INCREF(Py_None);
+ py_netmask = Py_None;
+ }
+
Py_INCREF(Py_None);
Py_INCREF(Py_None);
py_tuple = Py_BuildValue(
@@ -2993,7 +3117,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
py_nic_name,
family,
py_address,
- Py_None, // netmask (not supported)
+ py_netmask,
Py_None, // broadcast (not supported)
Py_None // ptp (not supported on Windows)
);
@@ -3004,6 +3128,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) {
goto error;
Py_DECREF(py_tuple);
Py_DECREF(py_address);
+ Py_DECREF(py_netmask);
pUnicast = pUnicast->Next;
}
@@ -3022,6 +3147,7 @@ error:
Py_XDECREF(py_tuple);
Py_XDECREF(py_address);
Py_XDECREF(py_nic_name);
+ Py_XDECREF(py_netmask);
return NULL;
}
@@ -3072,7 +3198,7 @@ psutil_net_if_stats(PyObject *self, PyObject *args) {
// Make a second call to GetIfTable to get the actual
// data we want.
if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) {
- PyErr_SetString(PyExc_RuntimeError, "GetIfTable() failed");
+ PyErr_SetString(PyExc_RuntimeError, "GetIfTable() syscall failed");
goto error;
}
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 7c7fff4b..2272c484 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -477,8 +477,8 @@ class WindowsService(object):
return d
# actions
- # XXX: the necessary C bindings for start() and stop() are implemented
- # but for now I prefer not to expose them.
+ # XXX: the necessary C bindings for start() and stop() are
+ # implemented but for now I prefer not to expose them.
# I may change my mind in the future. Reasons:
# - they require Administrator privileges
# - can't implement a timeout for stop() (unless by using a thread,
@@ -693,11 +693,11 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
if timeout is None:
- timeout = cext.INFINITE
+ cext_timeout = cext.INFINITE
else:
# WaitForSingleObject() expects time in milliseconds
- timeout = int(timeout * 1000)
- ret = cext.proc_wait(self.pid, timeout)
+ cext_timeout = int(timeout * 1000)
+ ret = cext.proc_wait(self.pid, cext_timeout)
if ret == WAIT_TIMEOUT:
raise TimeoutExpired(timeout, self.pid, self._name)
return ret
diff --git a/psutil/arch/bsd/freebsd.c b/psutil/arch/bsd/freebsd.c
index d335a7ad..456a50aa 100644
--- a/psutil/arch/bsd/freebsd.c
+++ b/psutil/arch/bsd/freebsd.c
@@ -25,7 +25,6 @@
#include <libutil.h> // process open files, shared libs (kinfo_getvmmap), cwd
#include <sys/cpuset.h>
-#include "freebsd.h"
#include "../../_psutil_common.h"
@@ -67,19 +66,6 @@ psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) {
}
-int
-psutil_raise_ad_or_nsp(long pid) {
- // Set exception to AccessDenied if pid exists else NoSuchProcess.
- int ret;
- ret = psutil_pid_exists(pid);
- if (ret == 0)
- NoSuchProcess();
- else if (ret == 1)
- AccessDenied();
- return ret;
-}
-
-
// remove spaces from string
static void psutil_remove_spaces(char *str) {
char *p1 = str;
@@ -106,10 +92,8 @@ psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) {
int err;
struct kinfo_proc *result;
int done;
- static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
- // Declaring name as const requires us to cast it when passing it to
- // sysctl because the prototype doesn't include the const modifier.
- size_t length;
+ int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
+ size_t length;
assert( procList != NULL);
assert(*procList == NULL);
@@ -281,34 +265,6 @@ error:
/*
- * Return 1 if PID exists in the current process list, else 0, -1
- * on error.
- * TODO: this should live in _psutil_posix.c but for some reason if I
- * move it there I get a "include undefined symbol" error.
- */
-int
-psutil_pid_exists(long pid) {
- int ret;
- if (pid < 0)
- return 0;
- ret = kill(pid , 0);
- if (ret == 0)
- return 1;
- else {
- if (ret == ESRCH)
- return 0;
- else if (ret == EPERM)
- return 1;
- else {
- PyErr_SetFromErrno(PyExc_OSError);
- return -1;
- }
- }
-}
-
-
-
-/*
* Return process pathname executable.
* Thanks to Robert N. M. Watson:
* http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
@@ -334,8 +290,14 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
size = sizeof(pathname);
error = sysctl(mib, 4, pathname, &size, NULL, 0);
if (error == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- return NULL;
+ if (errno == ENOENT) {
+ // see: https://github.com/giampaolo/psutil/issues/907
+ return Py_BuildValue("s", "");
+ }
+ else {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
}
if (size == 0 || strlen(pathname) == 0) {
ret = psutil_pid_exists(pid);
@@ -544,13 +506,14 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
if (kd == NULL) {
- PyErr_SetString(PyExc_RuntimeError, "kvm_open failed");
+ PyErr_SetString(PyExc_RuntimeError, "kvm_open() syscall failed");
return NULL;
}
if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
kvm_close(kd);
- PyErr_SetString(PyExc_RuntimeError, "kvm_getswapinfo failed");
+ PyErr_SetString(PyExc_RuntimeError,
+ "kvm_getswapinfo() syscall failed");
return NULL;
}
@@ -595,9 +558,10 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
if (psutil_kinfo_proc(pid, &kipp) == -1)
goto error;
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
goto error;
}
@@ -647,9 +611,10 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
if (psutil_kinfo_proc(pid, &kipp) == -1)
return NULL;
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
return NULL;
}
free(freep);
@@ -733,7 +698,8 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
if (py_retdict == NULL)
return NULL;
if (devstat_checkversion(NULL) < 0) {
- PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed");
+ PyErr_Format(PyExc_RuntimeError,
+ "devstat_checkversion() syscall failed");
goto error;
}
@@ -745,7 +711,7 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
bzero(stats.dinfo, sizeof(struct devinfo));
if (devstat_getdevs(NULL, &stats) == -1) {
- PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() failed");
+ PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() syscall failed");
goto error;
}
@@ -813,9 +779,10 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
if (psutil_kinfo_proc(pid, &kp) == -1)
goto error;
+ errno = 0;
freep = kinfo_getvmmap(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getvmmap() failed");
goto error;
}
for (i = 0; i < cnt; i++) {
@@ -971,7 +938,7 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
#else
long value = PyInt_AsLong(item);
#endif
- if (value == -1 && PyErr_Occurred())
+ if (value == -1 || PyErr_Occurred())
goto error;
CPU_SET(value, &cpu_set);
}
diff --git a/psutil/arch/bsd/freebsd.h b/psutil/arch/bsd/freebsd.h
index a539445d..e15706c6 100644
--- a/psutil/arch/bsd/freebsd.h
+++ b/psutil/arch/bsd/freebsd.h
@@ -8,10 +8,8 @@
typedef struct kinfo_proc kinfo_proc;
-static char *psutil_get_cmd_args(long pid, size_t *argsize);
int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
int psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc);
-int psutil_pid_exists(long pid);
//
PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
diff --git a/psutil/arch/bsd/freebsd_socks.c b/psutil/arch/bsd/freebsd_socks.c
index 8fe93db0..826b27f7 100644
--- a/psutil/arch/bsd/freebsd_socks.c
+++ b/psutil/arch/bsd/freebsd_socks.c
@@ -25,8 +25,7 @@
#include <net/if_media.h>
#include <libutil.h>
-#include "freebsd.h"
-#include "freebsd_socks.h"
+#include "../../_psutil_common.h"
#define HASHSIZE 1009
@@ -498,9 +497,10 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
goto error;
}
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
goto error;
}
diff --git a/psutil/arch/bsd/netbsd.c b/psutil/arch/bsd/netbsd.c
index 1e24d58d..2cf2ef22 100644
--- a/psutil/arch/bsd/netbsd.c
+++ b/psutil/arch/bsd/netbsd.c
@@ -39,8 +39,8 @@
#include <arpa/inet.h>
-#include "netbsd.h"
#include "netbsd_socks.h"
+#include "netbsd.h"
#include "../../_psutil_common.h"
#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
@@ -53,16 +53,6 @@
int
-psutil_raise_ad_or_nsp(long pid) {
- // Set exception to AccessDenied if pid exists else NoSuchProcess.
- if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
- else
- AccessDenied();
-}
-
-
-int
psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) {
// Fills a kinfo_proc struct based on process pid.
int ret;
@@ -124,31 +114,6 @@ kinfo_getfile(pid_t pid, int* cnt) {
}
-int
-psutil_pid_exists(pid_t pid) {
- // Return 1 if PID exists in the current process list, else 0, -1
- // on error.
- // TODO: this should live in _psutil_posix.c but for some reason if I
- // move it there I get a "include undefined symbol" error.
- int ret;
- if (pid < 0)
- return 0;
- ret = kill(pid , 0);
- if (ret == 0)
- return 1;
- else {
- if (ret == ESRCH)
- return 0;
- else if (ret == EPERM)
- return 1;
- else {
- PyErr_SetFromErrno(PyExc_OSError);
- return -1;
- }
- }
-}
-
-
// XXX: This is no longer used as per
// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820
// Current implementation uses /proc instead.
@@ -323,13 +288,14 @@ psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
if (kd == NULL) {
- PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() failed: %s", errbuf);
+ PyErr_Format(
+ PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf);
return errno;
}
result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt);
if (result == NULL) {
- PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() failed");
+ PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed");
kvm_close(kd);
return errno;
}
@@ -445,37 +411,32 @@ error:
}
+/*
+ * Virtual memory stats, taken from:
+ * https://github.com/satterly/zabbix-stats/blob/master/src/libs/zbxsysinfo/
+ * netbsd/memory.c
+ */
PyObject *
psutil_virtual_mem(PyObject *self, PyObject *args) {
- int64_t total_physmem;
size_t size;
struct uvmexp_sysctl uv;
- int physmem_mib[] = {CTL_HW, HW_PHYSMEM64};
- int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};
+ int mib[] = {CTL_VM, VM_UVMEXP2};
long pagesize = getpagesize();
- size = sizeof(total_physmem);
- if (sysctl(physmem_mib, 2, &total_physmem, &size, NULL, 0) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- return NULL;
- }
-
size = sizeof(uv);
- if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) {
+ if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return Py_BuildValue("KKKKKKKK",
- (unsigned long long) total_physmem, // total
- (unsigned long long) uv.free * pagesize, // free
- (unsigned long long) uv.active * pagesize, // active
- (unsigned long long) uv.inactive * pagesize, // inactive
- (unsigned long long) uv.wired * pagesize, // wired
- // taken from:
- // https://github.com/satterly/zabbix-stats/blob/master/src/libs/
- // zbxsysinfo/netbsd/memory.c
+ (unsigned long long) uv.npages << uv.pageshift, // total
+ (unsigned long long) uv.free << uv.pageshift, // free
+ (unsigned long long) uv.active << uv.pageshift, // active
+ (unsigned long long) uv.inactive << uv.pageshift, // inactive
+ (unsigned long long) uv.wired << uv.pageshift, // wired
(unsigned long long) uv.filepages + uv.execpages * pagesize, // cached
+ // These are determined from /proc/meminfo in Python.
(unsigned long long) 0, // buffers
(unsigned long long) 0 // shared
);
@@ -509,8 +470,8 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
swap_total = swap_free = 0;
for (i = 0; i < nswap; i++) {
if (swdev[i].se_flags & SWF_ENABLE) {
- swap_free += (swdev[i].se_nblks - swdev[i].se_inuse);
- swap_total += swdev[i].se_nblks;
+ swap_total += swdev[i].se_nblks * DEV_BSIZE;
+ swap_free += (swdev[i].se_nblks - swdev[i].se_inuse) * DEV_BSIZE;
}
}
free(swdev);
@@ -528,9 +489,9 @@ psutil_swap_mem(PyObject *self, PyObject *args) {
}
return Py_BuildValue("(LLLll)",
- swap_total * DEV_BSIZE,
- (swap_total - swap_free) * DEV_BSIZE,
- swap_free * DEV_BSIZE,
+ swap_total,
+ (swap_total - swap_free),
+ swap_free,
(long) uv.pgswapin * pagesize, // swap in
(long) uv.pgswapout * pagesize); // swap out
@@ -549,9 +510,10 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
return NULL;
}
free(freep);
@@ -562,6 +524,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
PyObject *
psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ // XXX: why static?
static int maxcpus;
int mib[3];
int ncpu;
diff --git a/psutil/arch/bsd/netbsd.h b/psutil/arch/bsd/netbsd.h
index 2c8edae6..96ad9f7d 100644
--- a/psutil/arch/bsd/netbsd.h
+++ b/psutil/arch/bsd/netbsd.h
@@ -13,11 +13,9 @@ int psutil_kinfo_proc(pid_t pid, kinfo_proc *proc);
struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt);
int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
char *psutil_get_cmd_args(pid_t pid, size_t *argsize);
-PyObject * psutil_get_cmdline(pid_t pid);
-int psutil_pid_exists(pid_t pid);
-int psutil_raise_ad_or_nsp(long pid);
//
+PyObject *psutil_get_cmdline(pid_t pid);
PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
diff --git a/psutil/arch/bsd/openbsd.c b/psutil/arch/bsd/openbsd.c
index 242045dc..dfa8999b 100644
--- a/psutil/arch/bsd/openbsd.c
+++ b/psutil/arch/bsd/openbsd.c
@@ -36,7 +36,6 @@
#include <err.h> // for warn() & err()
-#include "openbsd.h"
#include "../../_psutil_common.h"
#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
@@ -112,42 +111,6 @@ kinfo_getfile(long pid, int* cnt) {
}
-int
-psutil_pid_exists(long pid) {
- // Return 1 if PID exists in the current process list, else 0, -1
- // on error.
- // TODO: this should live in _psutil_posix.c but for some reason if I
- // move it there I get a "include undefined symbol" error.
- int ret;
- if (pid < 0)
- return 0;
- ret = kill(pid , 0);
- if (ret == 0)
- return 1;
- else {
- if (ret == ESRCH)
- return 0;
- else if (ret == EPERM)
- return 1;
- else {
- PyErr_SetFromErrno(PyExc_OSError);
- return -1;
- }
- }
-}
-
-
-int
-psutil_raise_ad_or_nsp(long pid) {
- // Set exception to AccessDenied if pid exists else NoSuchProcess.
- if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
- else
- AccessDenied();
- return 0;
-}
-
-
// ============================================================================
// APIS
// ============================================================================
@@ -284,7 +247,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (strstr(errbuf, "Permission denied") != NULL)
AccessDenied();
else
- PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() failed");
+ PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() syscall failed");
goto error;
}
@@ -295,7 +258,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (strstr(errbuf, "Permission denied") != NULL)
AccessDenied();
else
- PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() failed");
+ PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed");
goto error;
}
@@ -441,9 +404,10 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
if (psutil_kinfo_proc(pid, &kipp) == -1)
return NULL;
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
return NULL;
}
free(freep);
@@ -542,9 +506,10 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
goto error;
}
+ errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_ad_or_nsp(pid);
+ psutil_raise_for_pid(pid, "kinfo_getfile() failed");
goto error;
}
diff --git a/psutil/arch/bsd/openbsd.h b/psutil/arch/bsd/openbsd.h
index 923dfde1..4f870268 100644
--- a/psutil/arch/bsd/openbsd.h
+++ b/psutil/arch/bsd/openbsd.h
@@ -14,8 +14,6 @@ struct kinfo_file * kinfo_getfile(long pid, int* cnt);
int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
char **_psutil_get_argv(long pid);
PyObject * psutil_get_cmdline(long pid);
-int psutil_pid_exists(long pid);
-int psutil_raise_ad_or_nsp(long pid);
//
PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/process_info.c b/psutil/arch/osx/process_info.c
index 4c4ec88d..4b0b458b 100644
--- a/psutil/arch/osx/process_info.c
+++ b/psutil/arch/osx/process_info.c
@@ -24,38 +24,6 @@
/*
- * Return 1 if PID exists in the current process list, else 0, -1
- * on error.
- * TODO: this should live in _psutil_posix.c but for some reason if I
- * move it there I get a "include undefined symbol" error.
- */
-int
-psutil_pid_exists(long pid) {
- int ret;
- if (pid < 0)
- return 0;
- ret = kill(pid , 0);
- if (ret == 0)
- return 1;
- else {
- return 0;
- /*
- // This is how it is handled on other POSIX systems but it causes
- // test_halfway_terminated test to fail with AccessDenied.
- if (ret == ESRCH)
- return 0;
- else if (ret == EPERM)
- return 1;
- else {
- PyErr_SetFromErrno(PyExc_OSError);
- return -1;
- }
- */
- }
-}
-
-
-/*
* Returns a list of all BSD processes on the system. This routine
* allocates the list and puts it in *procList and a count of the
* number of entries in *procCount. You are responsible for freeing
@@ -65,12 +33,11 @@ psutil_pid_exists(long pid) {
*/
int
psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
- // Declaring mib as const requires use of a cast since the
- // sysctl prototype doesn't include the const modifier.
- static const int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
- size_t size, size2;
- void *ptr;
- int err, lim = 8; // some limit
+ int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
+ size_t size, size2;
+ void *ptr;
+ int err;
+ int lim = 8; // some limit
assert( procList != NULL);
assert(*procList == NULL);
@@ -142,7 +109,7 @@ PyObject *
psutil_get_cmdline(long pid) {
int mib[3];
int nargs;
- int len;
+ size_t len;
char *procargs = NULL;
char *arg_ptr;
char *arg_end;
@@ -172,7 +139,7 @@ psutil_get_cmdline(long pid) {
// read argument space
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
- mib[2] = pid;
+ mib[2] = (pid_t)pid;
if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
if (EINVAL == errno) {
// EINVAL == access denied OR nonexistent PID
@@ -271,7 +238,7 @@ psutil_get_environ(long pid) {
// read argument space
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
- mib[2] = pid;
+ mib[2] = (pid_t)pid;
if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
if (EINVAL == errno) {
// EINVAL == access denied OR nonexistent PID
@@ -310,7 +277,6 @@ psutil_get_environ(long pid) {
env_start = arg_ptr;
procenv = calloc(1, arg_end - arg_ptr);
-
if (procenv == NULL) {
PyErr_NoMemory();
goto error;
@@ -328,13 +294,19 @@ psutil_get_environ(long pid) {
}
#if PY_MAJOR_VERSION >= 3
- py_ret = PyUnicode_FromStringAndSize(procenv, arg_ptr - env_start + 1);
+ py_ret = PyUnicode_DecodeFSDefaultAndSize(
+ procenv, arg_ptr - env_start + 1);
#else
py_ret = PyString_FromStringAndSize(procenv, arg_ptr - env_start + 1);
#endif
- if (!py_ret)
+ if (!py_ret) {
+ // XXX: don't want to free() this as per:
+ // https://github.com/giampaolo/psutil/issues/926
+ // It sucks but not sure what else to do.
+ procargs = NULL;
goto error;
+ }
free(procargs);
free(procenv);
@@ -357,13 +329,13 @@ error:
int
-psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) {
+psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) {
int mib[4];
size_t len;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
- mib[3] = pid;
+ mib[3] = (pid_t)pid;
// fetch the info with sysctl()
len = sizeof(struct kinfo_proc);
@@ -385,26 +357,16 @@ psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) {
/*
- * A thin wrapper around proc_pidinfo()
+ * A wrapper around proc_pidinfo().
+ * Returns 0 on failure (and Python exception gets already set).
*/
int
-psutil_proc_pidinfo(long pid, int flavor, void *pti, int size) {
- int ret = proc_pidinfo((int)pid, flavor, 0, pti, size);
- if (ret == 0) {
- if (! psutil_pid_exists(pid)) {
- NoSuchProcess();
- return 0;
- }
- else {
- AccessDenied();
- return 0;
- }
- }
- else if (ret != size) {
- AccessDenied();
+psutil_proc_pidinfo(long pid, int flavor, uint64_t arg, void *pti, int size) {
+ errno = 0;
+ int ret = proc_pidinfo((int)pid, flavor, arg, pti, size);
+ if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
+ psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
return 0;
}
- else {
- return 1;
- }
+ return ret;
}
diff --git a/psutil/arch/osx/process_info.h b/psutil/arch/osx/process_info.h
index 8bc10ec1..bd2eef86 100644
--- a/psutil/arch/osx/process_info.h
+++ b/psutil/arch/osx/process_info.h
@@ -9,9 +9,9 @@
typedef struct kinfo_proc kinfo_proc;
int psutil_get_argmax(void);
-int psutil_get_kinfo_proc(pid_t pid, struct kinfo_proc *kp);
+int psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp);
int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount);
-int psutil_pid_exists(long pid);
-int psutil_proc_pidinfo(long pid, int flavor, void *pti, int size);
+int psutil_proc_pidinfo(
+ long pid, int flavor, uint64_t arg, void *pti, int size);
PyObject* psutil_get_cmdline(long pid);
PyObject* psutil_get_environ(long pid);
diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c
index 433da349..b260450e 100644
--- a/psutil/arch/windows/process_handles.c
+++ b/psutil/arch/windows/process_handles.c
@@ -138,8 +138,7 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) {
hHandle = &pHandleInfo->Handles[i];
// Check if this hHandle belongs to the PID the user specified.
- if (hHandle->UniqueProcessId != (HANDLE)dwPid ||
- hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE)
+ if (hHandle->UniqueProcessId != (HANDLE)dwPid)
goto loop_cleanup;
if (!DuplicateHandle(hProcess,
@@ -401,8 +400,7 @@ psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) {
hHandle = &pHandleInfo->Handles[i];
// Check if this hHandle belongs to the PID the user specified.
- if (hHandle->UniqueProcessId != (HANDLE)dwPid ||
- hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE)
+ if (hHandle->UniqueProcessId != (HANDLE)dwPid)
goto loop_cleanup;
if (!DuplicateHandle(hProcess,
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index 730a003a..007f1ba2 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -697,6 +697,8 @@ static int psutil_get_process_data(long pid,
goto error;
}
+ CloseHandle(hProcess);
+
*pdata = buffer;
*psize = size;
@@ -860,7 +862,8 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
}
if (status != 0) {
- PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed");
+ PyErr_Format(
+ PyExc_RuntimeError, "NtQuerySystemInformation() syscall failed");
goto error;
}
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index da71826c..c4ea5986 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
@@ -9,10 +8,12 @@
Test utilities.
"""
+from __future__ import print_function
import atexit
import contextlib
import errno
import functools
+import ipaddress # python >= 3.3 / requires "pip install ipaddress"
import os
import re
import shutil
@@ -28,10 +29,7 @@ import warnings
from socket import AF_INET
from socket import SOCK_DGRAM
from socket import SOCK_STREAM
-try:
- import ipaddress # python >= 3.3
-except ImportError:
- ipaddress = None
+
try:
from unittest import mock # py3
except ImportError:
@@ -39,7 +37,6 @@ except ImportError:
import psutil
from psutil import LINUX
-from psutil import OSX
from psutil import POSIX
from psutil import WINDOWS
from psutil._compat import PY3
@@ -63,6 +60,7 @@ if PY3:
else:
import imp as importlib
+
__all__ = [
# constants
'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'MEMORY_TOLERANCE', 'NO_RETRIES',
@@ -76,7 +74,7 @@ __all__ = [
'skip_on_access_denied', 'skip_on_not_implemented', 'retry_before_failing',
'run_test_module_by_name',
# fs utils
- 'chdir', 'safe_remove', 'safe_rmdir', 'create_temp_executable_file',
+ 'chdir', 'safe_rmpath', 'create_exe',
# subprocesses
'pyrun', 'reap_children', 'get_test_subprocess',
# os
@@ -104,16 +102,19 @@ AF_INET6 = getattr(socket, "AF_INET6")
AF_UNIX = getattr(socket, "AF_UNIX", None)
PYTHON = os.path.realpath(sys.executable)
DEVNULL = open(os.devnull, 'r+')
-TESTFN = os.path.join(os.getcwd(), "$testfile")
-TESTFN_UNICODE = TESTFN + "Æ’Å‘Å‘"
-TESTFILE_PREFIX = 'psutil-test-suite-'
-TOX = os.getenv('TOX') or '' in ('1', 'true')
-PYPY = '__pypy__' in sys.builtin_module_names
+
+TESTFILE_PREFIX = '$psutil'
+TESTFN = os.path.join(os.path.realpath(os.getcwd()), TESTFILE_PREFIX)
+_TESTFN = TESTFN + '-internal'
+TESTFN_UNICODE = TESTFN + "-Æ’Å‘Å‘"
if not PY3:
try:
- TESTFN_UNICODE = unicode(TESTFN_UNICODE, sys.getfilesystemencoding())
+ TESTFN_UNICODE = unicode(TESTFN, sys.getfilesystemencoding())
except UnicodeDecodeError:
- TESTFN_UNICODE = TESTFN + "???"
+ TESTFN_UNICODE = TESTFN + "-???"
+
+TOX = os.getenv('TOX') or '' in ('1', 'true')
+PYPY = '__pypy__' in sys.builtin_module_names
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..'))
@@ -128,8 +129,6 @@ TRAVIS = bool(os.environ.get('TRAVIS'))
# (http://www.appveyor.com/)
APPVEYOR = bool(os.environ.get('APPVEYOR'))
-if TRAVIS or 'tox' in sys.argv[0]:
- import ipaddress
if TRAVIS or APPVEYOR:
GLOBAL_TIMEOUT = GLOBAL_TIMEOUT * 4
VERBOSITY = 1 if os.getenv('SILENT') or TOX else 2
@@ -190,39 +189,26 @@ class ThreadTask(threading.Thread):
_subprocesses_started = set()
-def get_test_subprocess(cmd=None, wait=False, **kwds):
+def get_test_subprocess(cmd=None, **kwds):
"""Return a subprocess.Popen object to use in tests.
By default stdout and stderr are redirected to /dev/null and the
python interpreter is used as test process.
- If 'wait' is True attemps to make sure the process is in a
- reasonably initialized state.
+ It also attemps to make sure the process is in a reasonably
+ initialized state.
"""
- if cmd is None:
- pyline = ""
- if wait:
- pyline += "open(r'%s', 'w'); " % TESTFN
- # A process living for 30 secs. We sleep N times (as opposed to
- # once) in order to be nicer towards Windows which doesn't handle
- # interrupt signals properly.
- pyline += "import time; [time.sleep(0.01) for x in range(3000)];"
- cmd_ = [PYTHON, "-c", pyline]
- else:
- cmd_ = cmd
kwds.setdefault("stdin", DEVNULL)
kwds.setdefault("stdout", DEVNULL)
- kwds.setdefault("stderr", DEVNULL)
- sproc = subprocess.Popen(cmd_, **kwds)
- if wait:
- if cmd is None:
- stop_at = time.time() + 3
- while stop_at > time.time():
- if os.path.exists(TESTFN):
- break
- time.sleep(0.001)
- else:
- warn("couldn't make sure test file was actually created")
- else:
- wait_for_pid(sproc.pid)
+ if cmd is None:
+ assert not os.path.exists(_TESTFN)
+ pyline = "from time import sleep;"
+ pyline += "open(r'%s', 'w').close();" % _TESTFN
+ pyline += "sleep(60)"
+ cmd = [PYTHON, "-c", pyline]
+ sproc = subprocess.Popen(cmd, **kwds)
+ wait_for_file(_TESTFN, delete_file=True, empty=True)
+ else:
+ sproc = subprocess.Popen(cmd, **kwds)
+ wait_for_pid(sproc.pid)
_subprocesses_started.add(sproc)
return sproc
@@ -231,7 +217,7 @@ _testfiles = []
def pyrun(src):
- """Run python code 'src' in a separate interpreter.
+ """Run python 'src' code in a separate interpreter.
Return interpreter subprocess.
"""
if PY3:
@@ -267,18 +253,23 @@ def sh(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
def reap_children(recursive=False):
- """Kill any subprocess started by this test suite and ensure that
- no zombies stick around to hog resources and create problems when
- looking for refleaks.
+ """Terminate and wait() any subprocess started by this test suite
+ and ensure that no zombies stick around to hog resources and
+ create problems when looking for refleaks.
+
+ If resursive is True it also tries to terminate and wait()
+ all grandchildren started by this process.
"""
- # Get the children here, before terminating the sub processes
- # as we don't want to lose the intermediate reference in case
- # of grand children.
+ # Get the children here, before terminating the children sub
+ # processes as we don't want to lose the intermediate reference
+ # in case of grandchildren.
if recursive:
children = psutil.Process().children(recursive=True)
else:
children = []
+ # Terminate subprocess.Popen instances "cleanly" by closing their
+ # fds and wiat()ing for them in order to avoid zombies.
subprocs = _subprocesses_started.copy()
_subprocesses_started.clear()
for subp in subprocs:
@@ -287,12 +278,23 @@ def reap_children(recursive=False):
except OSError as err:
if err.errno != errno.ESRCH:
raise
+ if subp.stdout:
+ subp.stdout.close()
+ if subp.stderr:
+ subp.stderr.close()
try:
- subp.wait()
- except OSError as err:
- if err.errno != errno.ECHILD:
- raise
+ # Flushing a BufferedWriter may raise an error.
+ if subp.stdin:
+ subp.stdin.close()
+ finally:
+ # Wait for the process to terminate, to avoid zombies.
+ try:
+ subp.wait()
+ except OSError as err:
+ if err.errno != errno.ECHILD:
+ raise
+ # Terminates grandchildren.
if children:
for p in children:
try:
@@ -301,14 +303,15 @@ def reap_children(recursive=False):
pass
gone, alive = psutil.wait_procs(children, timeout=GLOBAL_TIMEOUT)
for p in alive:
- warn("couldn't terminate process %s" % p)
+ warn("couldn't terminate process %r; attempting kill()" % p)
try:
p.kill()
except psutil.NoSuchProcess:
pass
- _, alive = psutil.wait_procs(alive, timeout=GLOBAL_TIMEOUT)
- if alive:
- warn("couldn't not kill processes %s" % str(alive))
+ _, alive = psutil.wait_procs(alive, timeout=GLOBAL_TIMEOUT)
+ if alive:
+ for p in alive:
+ warn("process %r survived kill()" % p)
# ===================================================================
@@ -370,50 +373,98 @@ else:
# ===================================================================
-def wait_for_pid(pid, timeout=GLOBAL_TIMEOUT):
+class retry(object):
+ """A retry decorator."""
+
+ def __init__(self,
+ exception=Exception,
+ timeout=None,
+ retries=None,
+ interval=0.001,
+ logfun=lambda s: print(s, file=sys.stderr),
+ ):
+ if timeout and retries:
+ raise ValueError("timeout and retries args are mutually exclusive")
+ self.exception = exception
+ self.timeout = timeout
+ self.retries = retries
+ self.interval = interval
+ self.logfun = logfun
+
+ def __iter__(self):
+ if self.timeout:
+ stop_at = time.time() + self.timeout
+ while time.time() < stop_at:
+ yield
+ elif self.retries:
+ for _ in range(self.retries):
+ yield
+ else:
+ while True:
+ yield
+
+ def sleep(self):
+ if self.interval is not None:
+ time.sleep(self.interval)
+
+ def __call__(self, fun):
+ @functools.wraps(fun)
+ def wrapper(*args, **kwargs):
+ exc = None
+ for _ in self:
+ try:
+ return fun(*args, **kwargs)
+ except self.exception as _:
+ exc = _
+ if self.logfun is not None:
+ self.logfun(exc)
+ self.sleep()
+ else:
+ if PY3:
+ raise exc
+ else:
+ raise
+
+ # This way the user of the decorated function can change config
+ # parameters.
+ wrapper.decorator = self
+ return wrapper
+
+
+@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT,
+ interval=0.001)
+def wait_for_pid(pid):
"""Wait for pid to show up in the process list then return.
Used in the test suite to give time the sub process to initialize.
"""
- raise_at = time.time() + timeout
- while True:
- if pid in psutil.pids():
- # give it one more iteration to allow full initialization
- time.sleep(0.01)
- return
- time.sleep(0.0001)
- if time.time() >= raise_at:
- raise RuntimeError("Timed out")
-
-
-def wait_for_file(fname, timeout=GLOBAL_TIMEOUT, delete_file=True):
- """Wait for a file to be written on disk."""
- stop_at = time.time() + timeout
- while time.time() < stop_at:
- try:
- with open(fname, "r") as f:
- data = f.read()
- if not data:
- continue
- if delete_file:
- os.remove(fname)
- return data
- except IOError:
- time.sleep(0.001)
- raise RuntimeError(
- "timed out after %s secs (couldn't read file)" % timeout)
-
-
-def call_until(fun, expr, timeout=GLOBAL_TIMEOUT):
+ psutil.Process(pid)
+ if WINDOWS:
+ # give it some more time to allow better initialization
+ time.sleep(0.01)
+
+
+@retry(exception=(EnvironmentError, AssertionError), logfun=None,
+ timeout=GLOBAL_TIMEOUT, interval=0.001)
+def wait_for_file(fname, delete_file=True, empty=False):
+ """Wait for a file to be written on disk with some content."""
+ with open(fname, "rb") as f:
+ data = f.read()
+ if not empty:
+ assert data
+ if delete_file:
+ os.remove(fname)
+ return data
+
+
+@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT,
+ interval=0.001)
+def call_until(fun, expr):
"""Keep calling function for timeout secs and exit if eval()
expression is True.
"""
- stop_at = time.time() + timeout
- while time.time() < stop_at:
- ret = fun()
- if eval(expr):
- return ret
- time.sleep(0.001)
- raise RuntimeError('timed out after %s secs (ret=%r)' % (timeout, ret))
+ ret = fun()
+ assert eval(expr)
+ return ret
# ===================================================================
@@ -421,22 +472,14 @@ def call_until(fun, expr, timeout=GLOBAL_TIMEOUT):
# ===================================================================
-def safe_remove(file):
- "Convenience function for removing temporary test files"
+def safe_rmpath(path):
+ "Convenience function for removing temporary test files or dirs"
try:
- os.remove(file)
- except OSError as err:
- if err.errno != errno.ENOENT:
- # # file is being used by another process
- # if WINDOWS and isinstance(err, WindowsError) and err.errno == 13:
- # return
- raise
-
-
-def safe_rmdir(dir):
- "Convenience function for removing temporary test directories"
- try:
- os.rmdir(dir)
+ st = os.stat(path)
+ if stat.S_ISDIR(st.st_mode):
+ os.rmdir(path)
+ else:
+ os.remove(path)
except OSError as err:
if err.errno != errno.ENOENT:
raise
@@ -444,7 +487,7 @@ def safe_rmdir(dir):
@contextlib.contextmanager
def chdir(dirname):
- """Context manager which temporarily changes the current directory."""
+ "Context manager which temporarily changes the current directory."
curdir = os.getcwd()
try:
os.chdir(dirname)
@@ -453,28 +496,56 @@ def chdir(dirname):
os.chdir(curdir)
+def create_exe(outpath, c_code=None):
+ assert not os.path.exists(outpath), outpath
+ if which("gcc"):
+ if c_code is None:
+ c_code = textwrap.dedent(
+ """
+ #include <unistd.h>
+ int main() {
+ pause();
+ return 1;
+ }
+ """)
+ with tempfile.NamedTemporaryFile(
+ suffix='.c', delete=False, mode='wt') as f:
+ f.write(c_code)
+ try:
+ subprocess.check_call(["gcc", f.name, "-o", outpath])
+ finally:
+ safe_rmpath(f.name)
+ else:
+ # fallback - use python's executable
+ shutil.copyfile(sys.executable, outpath)
+ if POSIX:
+ st = os.stat(outpath)
+ os.chmod(outpath, st.st_mode | stat.S_IEXEC)
+
+
# ===================================================================
# --- testing
# ===================================================================
-def retry_before_failing(ntimes=None):
+class TestCase(unittest.TestCase):
+
+ def __str__(self):
+ return "%s.%s.%s" % (
+ self.__class__.__module__, self.__class__.__name__,
+ self._testMethodName)
+
+
+# Hack that overrides default unittest.TestCase in order to print
+# a full path representation of the single unit tests being run.
+unittest.TestCase = TestCase
+
+
+def retry_before_failing(retries=NO_RETRIES):
"""Decorator which runs a test function and retries N times before
actually failing.
"""
- def decorator(fun):
- @functools.wraps(fun)
- def wrapper(*args, **kwargs):
- times = ntimes or NO_RETRIES
- assert times, times
- for x in range(times):
- try:
- return fun(*args, **kwargs)
- except AssertionError as _:
- err = _
- raise err
- return wrapper
- return decorator
+ return retry(exception=AssertionError, timeout=None, retries=retries)
def run_test_module_by_name(name):
@@ -535,16 +606,14 @@ def check_net_address(addr, family):
assert len(octs) == 4, addr
for num in octs:
assert 0 <= num <= 255, addr
- if ipaddress:
- if not PY3:
- addr = unicode(addr)
- ipaddress.IPv4Address(addr)
+ if not PY3:
+ addr = unicode(addr)
+ ipaddress.IPv4Address(addr)
elif family == AF_INET6:
assert isinstance(addr, str), addr
- if ipaddress:
- if not PY3:
- addr = unicode(addr)
- ipaddress.IPv6Address(addr)
+ if not PY3:
+ addr = unicode(addr)
+ ipaddress.IPv6Address(addr)
elif family == psutil.AF_LINK:
assert re.match('([a-fA-F0-9]{2}[:|\-]?){6}', addr) is not None, addr
else:
@@ -611,48 +680,16 @@ def check_connection_ntuple(conn):
assert dupsock.type == conn.type
-def create_temp_executable_file(suffix, c_code=None):
- tmpdir = None
- if TRAVIS and OSX:
- tmpdir = "/private/tmp"
- fd, path = tempfile.mkstemp(
- prefix='psu', suffix=suffix, dir=tmpdir)
- os.close(fd)
-
- if which("gcc"):
- if c_code is None:
- c_code = textwrap.dedent(
- """
- #include <unistd.h>
- void main() {
- pause();
- }
- """)
- fd, c_file = tempfile.mkstemp(
- prefix='psu', suffix='.c', dir=tmpdir)
- os.close(fd)
- with open(c_file, "w") as f:
- f.write(c_code)
- subprocess.check_call(["gcc", c_file, "-o", path])
- safe_remove(c_file)
- else:
- # fallback - use python's executable
- shutil.copyfile(sys.executable, path)
- if POSIX:
- st = os.stat(path)
- os.chmod(path, st.st_mode | stat.S_IEXEC)
- return path
-
-
def cleanup():
- reap_children(recursive=True)
- safe_remove(TESTFN)
- try:
- safe_rmdir(TESTFN_UNICODE)
- except UnicodeEncodeError:
- pass
+ for name in os.listdir('.'):
+ if name.startswith(TESTFILE_PREFIX):
+ try:
+ safe_rmpath(name)
+ except UnicodeEncodeError as exc:
+ warn(exc)
for path in _testfiles:
- safe_remove(path)
+ safe_rmpath(path)
+
atexit.register(cleanup)
atexit.register(lambda: DEVNULL.close())
diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py
index c4dd88b3..1c282f68 100644..100755
--- a/psutil/tests/runner.py
+++ b/psutil/tests/runner.py
@@ -19,6 +19,8 @@ testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
x.startswith('test_memory_leaks')]
suite = unittest.TestSuite()
for tm in testmodules:
+ # ...so that "make test" will print the full test paths
+ tm = "psutil.tests.%s" % tm
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
result = unittest.TextTestRunner(verbosity=VERBOSITY).run(suite)
success = result.wasSuccessful()
diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py
index 53f830d3..244672e6 100644..100755
--- a/psutil/tests/test_bsd.py
+++ b/psutil/tests/test_bsd.py
@@ -12,6 +12,7 @@
import datetime
import os
+import re
import subprocess
import sys
import time
@@ -73,7 +74,7 @@ def muse(field):
# =====================================================================
-@unittest.skipUnless(BSD, "not a BSD system")
+@unittest.skipUnless(BSD, "BSD only")
class BSDSpecificTestCase(unittest.TestCase):
"""Generic tests common to all BSD variants."""
@@ -124,21 +125,34 @@ class BSDSpecificTestCase(unittest.TestCase):
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)
+ self.assertEqual(stats.mtu,
+ int(re.findall('mtu (\d+)', out)[0]))
+
# =====================================================================
# --- FreeBSD
# =====================================================================
-@unittest.skipUnless(FREEBSD, "not a FreeBSD system")
+@unittest.skipUnless(FREEBSD, "FREEBSD only")
class FreeBSDSpecificTestCase(unittest.TestCase):
@classmethod
@@ -149,15 +163,8 @@ class FreeBSDSpecificTestCase(unittest.TestCase):
def tearDownClass(cls):
reap_children()
- 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())
-
@retry_before_failing()
- def test_memory_maps(self):
+ 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:]
@@ -171,17 +178,17 @@ class FreeBSDSpecificTestCase(unittest.TestCase):
if not map.path.startswith('['):
self.assertEqual(fields[10], map.path)
- def test_exe(self):
+ 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_cmdline(self):
+ 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_uids_gids(self):
+ 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)
@@ -194,6 +201,46 @@ class FreeBSDSpecificTestCase(unittest.TestCase):
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()
@@ -234,47 +281,47 @@ class FreeBSDSpecificTestCase(unittest.TestCase):
# --- virtual_memory(); tests against muse
- @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(MUSE_AVAILABLE, "muse not installed")
def test_muse_vmem_total(self):
num = muse('Total')
self.assertEqual(psutil.virtual_memory().total, num)
- @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(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.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(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.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(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.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(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.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(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.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available")
+ @unittest.skipUnless(MUSE_AVAILABLE, "muse not installed")
@retry_before_failing()
def test_muse_vmem_buffers(self):
num = muse('Buffer')
@@ -301,12 +348,22 @@ class FreeBSDSpecificTestCase(unittest.TestCase):
# self.assertAlmostEqual(psutil.cpu_stats().traps,
# sysctl('vm.stats.sys.v_trap'), delta=1000)
+ # --- 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())
+
# =====================================================================
# --- OpenBSD
# =====================================================================
-@unittest.skipUnless(OPENBSD, "not an OpenBSD system")
+
+@unittest.skipUnless(OPENBSD, "OPENBSD only")
class OpenBSDSpecificTestCase(unittest.TestCase):
def test_boot_time(self):
@@ -320,7 +377,8 @@ class OpenBSDSpecificTestCase(unittest.TestCase):
# --- NetBSD
# =====================================================================
-@unittest.skipUnless(NETBSD, "not a NetBSD system")
+
+@unittest.skipUnless(NETBSD, "NETBSD only")
class NetBSDSpecificTestCase(unittest.TestCase):
def parse_meminfo(self, look_for):
@@ -330,31 +388,38 @@ class NetBSDSpecificTestCase(unittest.TestCase):
return int(line.split()[1]) * 1024
raise ValueError("can't find %s" % look_for)
- # XXX - failing tests
-
- # def test_vmem_total(self):
- # self.assertEqual(
- # psutil.virtual_memory().total, self.parse_meminfo("MemTotal:"))
+ def test_vmem_total(self):
+ self.assertEqual(
+ psutil.virtual_memory().total, self.parse_meminfo("MemTotal:"))
- # def test_vmem_free(self):
- # self.assertEqual(
- # psutil.virtual_memory().buffers, self.parse_meminfo("MemFree:"))
+ def test_vmem_free(self):
+ self.assertAlmostEqual(
+ psutil.virtual_memory().free, self.parse_meminfo("MemFree:"),
+ delta=MEMORY_TOLERANCE)
def test_vmem_buffers(self):
- self.assertEqual(
- psutil.virtual_memory().buffers, self.parse_meminfo("Buffers:"))
+ self.assertAlmostEqual(
+ psutil.virtual_memory().buffers, self.parse_meminfo("Buffers:"),
+ delta=MEMORY_TOLERANCE)
def test_vmem_shared(self):
- self.assertEqual(
- psutil.virtual_memory().shared, self.parse_meminfo("MemShared:"))
+ self.assertAlmostEqual(
+ psutil.virtual_memory().shared, self.parse_meminfo("MemShared:"),
+ delta=MEMORY_TOLERANCE)
def test_swapmem_total(self):
- self.assertEqual(
- psutil.swap_memory().total, self.parse_meminfo("SwapTotal:"))
+ self.assertAlmostEqual(
+ psutil.swap_memory().total, self.parse_meminfo("SwapTotal:"),
+ delta=MEMORY_TOLERANCE)
def test_swapmem_free(self):
- self.assertEqual(
- psutil.swap_memory().free, self.parse_meminfo("SwapFree:"))
+ 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:
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index f6c26365..fe2f0810 100644..100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -6,6 +6,8 @@
"""Linux specific tests."""
+from __future__ import division
+import collections
import contextlib
import errno
import io
@@ -34,7 +36,7 @@ from psutil.tests import pyrun
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_remove
+from psutil.tests import safe_rmpath
from psutil.tests import sh
from psutil.tests import skip_on_not_implemented
from psutil.tests import TESTFN
@@ -55,6 +57,7 @@ if LINUX:
# utils
# =====================================================================
+
def get_ipv4_address(ifname):
import fcntl
ifname = ifname[:15]
@@ -90,11 +93,13 @@ def free_swap():
"""Parse 'free' cmd and return swap memory's s total, used and free
values.
"""
- lines = sh('free').split('\n')
+ out = sh('free -b')
+ lines = out.split('\n')
for line in lines:
if line.startswith('Swap'):
_, total, used, free = line.split()
- return (int(total) * 1024, int(used) * 1024, int(free) * 1024)
+ nt = collections.namedtuple('free', 'total used free')
+ return nt(int(total), int(used), int(free))
raise ValueError(
"can't find 'Swap' in 'free' output:\n%s" % '\n'.join(lines))
@@ -107,105 +112,300 @@ def free_physmem():
# and 'cached' memory which may have different positions so we
# do not return them.
# https://github.com/giampaolo/psutil/issues/538#issuecomment-57059946
- lines = sh('free').split('\n')
+ out = sh('free -b')
+ lines = out.split('\n')
for line in lines:
if line.startswith('Mem'):
total, used, free, shared = \
- [int(x) * 1024 for x in line.split()[1:5]]
- return (total, used, free, shared)
+ [int(x) for x in line.split()[1:5]]
+ nt = collections.namedtuple(
+ 'free', 'total used free shared output')
+ return nt(total, used, free, shared, out)
raise ValueError(
"can't find 'Mem' in 'free' output:\n%s" % '\n'.join(lines))
+def vmstat(stat):
+ out = sh("vmstat -s")
+ for line in out.split("\n"):
+ line = line.strip()
+ if stat in line:
+ return int(line.split(' ')[0])
+ raise ValueError("can't find %r in 'vmstat' output" % stat)
+
+
+def get_free_version_info():
+ out = sh("free -V").strip()
+ return tuple(map(int, out.split()[-1].split('.')))
+
+
# =====================================================================
# system virtual memory
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestSystemVirtualMemory(unittest.TestCase):
def test_total(self):
- total, used, free, shared = free_physmem()
- self.assertEqual(total, psutil.virtual_memory().total)
-
+ # free_value = free_physmem().total
+ # psutil_value = psutil.virtual_memory().total
+ # self.assertEqual(free_value, psutil_value)
+ vmstat_value = vmstat('total memory') * 1024
+ psutil_value = psutil.virtual_memory().total
+ self.assertAlmostEqual(vmstat_value, psutil_value)
+
+ # Older versions of procps used slab memory to calculate used memory.
+ # This got changed in:
+ # https://gitlab.com/procps-ng/procps/commit/
+ # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+ @unittest.skipUnless(
+ LINUX and get_free_version_info() >= (3, 3, 12), "old free version")
@retry_before_failing()
def test_used(self):
- total, used, free, shared = free_physmem()
- self.assertAlmostEqual(used, psutil.virtual_memory().used,
- delta=MEMORY_TOLERANCE)
+ free = free_physmem()
+ free_value = free.used
+ psutil_value = psutil.virtual_memory().used
+ self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE,
+ msg='%s %s \n%s' % (free_value, psutil_value, free.output))
@retry_before_failing()
def test_free(self):
- total, used, free, shared = free_physmem()
- self.assertAlmostEqual(free, psutil.virtual_memory().free,
- delta=MEMORY_TOLERANCE)
+ # _, _, free_value, _ = free_physmem()
+ # psutil_value = psutil.virtual_memory().free
+ # self.assertAlmostEqual(
+ # free_value, psutil_value, delta=MEMORY_TOLERANCE)
+ vmstat_value = vmstat('free memory') * 1024
+ psutil_value = psutil.virtual_memory().free
+ self.assertAlmostEqual(
+ vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
def test_buffers(self):
- buffers = int(sh('vmstat').split('\n')[2].split()[4]) * 1024
- self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers,
- delta=MEMORY_TOLERANCE)
+ vmstat_value = vmstat('buffer memory') * 1024
+ psutil_value = psutil.virtual_memory().buffers
+ self.assertAlmostEqual(
+ vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
- def test_cached(self):
- cached = int(sh('vmstat').split('\n')[2].split()[5]) * 1024
- self.assertAlmostEqual(cached, psutil.virtual_memory().cached,
- delta=MEMORY_TOLERANCE)
+ def test_active(self):
+ vmstat_value = vmstat('active memory') * 1024
+ psutil_value = psutil.virtual_memory().active
+ self.assertAlmostEqual(
+ vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
+
+ @retry_before_failing()
+ def test_inactive(self):
+ vmstat_value = vmstat('inactive memory') * 1024
+ psutil_value = psutil.virtual_memory().inactive
+ self.assertAlmostEqual(
+ vmstat_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
- @unittest.skipIf(TRAVIS, "fails on travis")
def test_shared(self):
- total, used, free, shared = free_physmem()
- self.assertAlmostEqual(shared, psutil.virtual_memory().shared,
- delta=MEMORY_TOLERANCE)
+ free = free_physmem()
+ free_value = free.shared
+ if free_value == 0:
+ raise unittest.SkipTest("free does not support 'shared' column")
+ psutil_value = psutil.virtual_memory().shared
+ self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE,
+ msg='%s %s \n%s' % (free_value, psutil_value, free.output))
- # --- mocked tests
+ @retry_before_failing()
+ def test_available(self):
+ # "free" output format has changed at some point:
+ # https://github.com/giampaolo/psutil/issues/538#issuecomment-147192098
+ out = sh("free -b")
+ lines = out.split('\n')
+ if 'available' not in lines[0]:
+ raise unittest.SkipTest("free does not support 'available' column")
+ else:
+ free_value = int(lines[1].split()[-1])
+ psutil_value = psutil.virtual_memory().available
+ self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE,
+ msg='%s %s \n%s' % (free_value, psutil_value, out))
def test_warnings_mocked(self):
- with mock.patch('psutil._pslinux.open', create=True) as m:
+ def open_mock(name, *args, **kwargs):
+ if name == '/proc/meminfo':
+ return io.BytesIO(textwrap.dedent("""\
+ Active(anon): 6145416 kB
+ Active(file): 2950064 kB
+ Buffers: 287952 kB
+ Inactive(anon): 574764 kB
+ Inactive(file): 1567648 kB
+ MemAvailable: 6574984 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ SReclaimable: 346648 kB
+ """).encode())
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
with warnings.catch_warnings(record=True) as ws:
warnings.simplefilter("always")
- ret = psutil._pslinux.virtual_memory()
+ ret = psutil.virtual_memory()
assert m.called
self.assertEqual(len(ws), 1)
w = ws[0]
self.assertTrue(w.filename.endswith('psutil/_pslinux.py'))
self.assertIn(
"memory stats couldn't be determined", str(w.message))
+ self.assertIn("cached", str(w.message))
+ self.assertIn("shared", str(w.message))
+ self.assertIn("active", str(w.message))
+ self.assertIn("inactive", str(w.message))
self.assertEqual(ret.cached, 0)
self.assertEqual(ret.active, 0)
self.assertEqual(ret.inactive, 0)
+ self.assertEqual(ret.shared, 0)
+
+ def test_avail_old_percent(self):
+ # Make sure that our calculation of avail mem for old kernels
+ # is off by max 10%.
+ from psutil._pslinux import calculate_avail_vmem
+ from psutil._pslinux import open_binary
+
+ mems = {}
+ with open_binary('/proc/meminfo') as f:
+ for line in f:
+ fields = line.split()
+ mems[fields[0]] = int(fields[1]) * 1024
+
+ a = calculate_avail_vmem(mems)
+ if b'MemAvailable:' in mems:
+ b = mems[b'MemAvailable:']
+ diff_percent = abs(a - b) / a * 100
+ self.assertLess(diff_percent, 10)
+
+ def test_avail_old_comes_from_kernel(self):
+ # Make sure "MemAvailable:" coluimn is used instead of relying
+ # on our internal algorithm to calculate avail mem.
+ def open_mock(name, *args, **kwargs):
+ if name == "/proc/meminfo":
+ return io.BytesIO(textwrap.dedent("""\
+ Active: 9444728 kB
+ Active(anon): 6145416 kB
+ Active(file): 2950064 kB
+ Buffers: 287952 kB
+ Cached: 4818144 kB
+ Inactive(file): 1578132 kB
+ Inactive(anon): 574764 kB
+ Inactive(file): 1567648 kB
+ MemAvailable: 6574984 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ Shmem: 577588 kB
+ SReclaimable: 346648 kB
+ """).encode())
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ ret = psutil.virtual_memory()
+ assert m.called
+ self.assertEqual(ret.available, 6574984 * 1024)
+
+ def test_avail_old_missing_fields(self):
+ # Remove Active(file), Inactive(file) and SReclaimable
+ # from /proc/meminfo and make sure the fallback is used
+ # (free + cached),
+ def open_mock(name, *args, **kwargs):
+ if name == "/proc/meminfo":
+ return io.BytesIO(textwrap.dedent("""\
+ Active: 9444728 kB
+ Active(anon): 6145416 kB
+ Buffers: 287952 kB
+ Cached: 4818144 kB
+ Inactive(file): 1578132 kB
+ Inactive(anon): 574764 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ Shmem: 577588 kB
+ """).encode())
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ ret = psutil.virtual_memory()
+ assert m.called
+ self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024)
+
+ def test_avail_old_missing_zoneinfo(self):
+ # Remove /proc/zoneinfo file. Make sure fallback is used
+ # (free + cached).
+ def open_mock(name, *args, **kwargs):
+ if name == "/proc/meminfo":
+ return io.BytesIO(textwrap.dedent("""\
+ Active: 9444728 kB
+ Active(anon): 6145416 kB
+ Active(file): 2950064 kB
+ Buffers: 287952 kB
+ Cached: 4818144 kB
+ Inactive(file): 1578132 kB
+ Inactive(anon): 574764 kB
+ Inactive(file): 1567648 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ Shmem: 577588 kB
+ SReclaimable: 346648 kB
+ """).encode())
+ elif name == "/proc/zoneinfo":
+ raise IOError(errno.ENOENT, 'no such file or directory')
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ ret = psutil.virtual_memory()
+ assert m.called
+ self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024)
# =====================================================================
# system swap memory
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestSystemSwapMemory(unittest.TestCase):
def test_total(self):
- total, used, free = free_swap()
- return self.assertAlmostEqual(total, psutil.swap_memory().total,
- delta=MEMORY_TOLERANCE)
+ free_value = free_swap().total
+ psutil_value = psutil.swap_memory().total
+ return self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
def test_used(self):
- total, used, free = free_swap()
- return self.assertAlmostEqual(used, psutil.swap_memory().used,
- delta=MEMORY_TOLERANCE)
+ free_value = free_swap().used
+ psutil_value = psutil.swap_memory().used
+ return self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE)
@retry_before_failing()
def test_free(self):
- total, used, free = free_swap()
- return self.assertAlmostEqual(free, psutil.swap_memory().free,
- delta=MEMORY_TOLERANCE)
+ free_value = free_swap().free
+ psutil_value = psutil.swap_memory().free
+ return self.assertAlmostEqual(
+ free_value, psutil_value, delta=MEMORY_TOLERANCE)
def test_warnings_mocked(self):
with mock.patch('psutil._pslinux.open', create=True) as m:
with warnings.catch_warnings(record=True) as ws:
warnings.simplefilter("always")
- ret = psutil._pslinux.swap_memory()
+ ret = psutil.swap_memory()
assert m.called
self.assertEqual(len(ws), 1)
w = ws[0]
@@ -239,7 +439,8 @@ class TestSystemSwapMemory(unittest.TestCase):
# system CPU
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestSystemCPU(unittest.TestCase):
@unittest.skipIf(TRAVIS, "unknown failure on travis")
@@ -320,10 +521,31 @@ class TestSystemCPU(unittest.TestCase):
# =====================================================================
+# system CPU stats
+# =====================================================================
+
+
+@unittest.skipUnless(LINUX, "LINUX only")
+class TestSystemCPUStats(unittest.TestCase):
+
+ @unittest.skipIf(TRAVIS, "fails on Travis")
+ def test_ctx_switches(self):
+ vmstat_value = vmstat("context switches")
+ psutil_value = psutil.cpu_stats().ctx_switches
+ self.assertAlmostEqual(vmstat_value, psutil_value, delta=500)
+
+ def test_interrupts(self):
+ vmstat_value = vmstat("interrupts")
+ psutil_value = psutil.cpu_stats().interrupts
+ self.assertAlmostEqual(vmstat_value, psutil_value, delta=500)
+
+
+# =====================================================================
# system network
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestSystemNetwork(unittest.TestCase):
def test_net_if_addrs_ips(self):
@@ -342,10 +564,11 @@ class TestSystemNetwork(unittest.TestCase):
except RuntimeError:
pass
else:
- self.assertEqual(stats.isup, 'RUNNING' in out)
+ self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
self.assertEqual(stats.mtu,
int(re.findall('MTU:(\d+)', out)[0]))
+ @retry_before_failing()
def test_net_io_counters(self):
def ifconfig(nic):
ret = {}
@@ -366,13 +589,13 @@ class TestSystemNetwork(unittest.TestCase):
except RuntimeError:
continue
self.assertAlmostEqual(
- stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024)
+ stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5)
self.assertAlmostEqual(
- stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024)
+ stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5)
self.assertAlmostEqual(
- stats.packets_recv, ifconfig_ret['packets_recv'], delta=512)
+ stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024)
self.assertAlmostEqual(
- stats.packets_sent, ifconfig_ret['packets_sent'], delta=512)
+ stats.packets_sent, ifconfig_ret['packets_sent'], delta=1024)
self.assertAlmostEqual(
stats.errin, ifconfig_ret['errin'], delta=10)
self.assertAlmostEqual(
@@ -420,7 +643,6 @@ class TestSystemNetwork(unittest.TestCase):
"""))
else:
return orig_open(name, *args, **kwargs)
- return orig_open(name, *args)
orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
@@ -433,7 +655,8 @@ class TestSystemNetwork(unittest.TestCase):
# system disk
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestSystemDisks(unittest.TestCase):
@unittest.skipUnless(
@@ -502,7 +725,6 @@ class TestSystemDisks(unittest.TestCase):
u(" 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12"))
else:
return orig_open(name, *args, **kwargs)
- return orig_open(name, *args)
orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
@@ -535,7 +757,6 @@ class TestSystemDisks(unittest.TestCase):
u(" 3 0 hda 1 2 3 4 5 6 7 8 9 10 11"))
else:
return orig_open(name, *args, **kwargs)
- return orig_open(name, *args)
orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
@@ -570,7 +791,6 @@ class TestSystemDisks(unittest.TestCase):
u(" 3 1 hda 1 2 3 4"))
else:
return orig_open(name, *args, **kwargs)
- return orig_open(name, *args)
orig_open = open
patch_point = 'builtins.open' if PY3 else '__builtin__.open'
@@ -593,9 +813,15 @@ class TestSystemDisks(unittest.TestCase):
# misc
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestMisc(unittest.TestCase):
+ def test_boot_time(self):
+ vmstat_value = vmstat('boot time')
+ psutil_value = psutil.boot_time()
+ self.assertEqual(int(vmstat_value), int(psutil_value))
+
@mock.patch('psutil.traceback.print_exc')
def test_no_procfs_on_import(self, tb):
my_procfs = tempfile.mkdtemp()
@@ -767,11 +993,12 @@ class TestMisc(unittest.TestCase):
# test process
# =====================================================================
-@unittest.skipUnless(LINUX, "not a Linux system")
+
+@unittest.skipUnless(LINUX, "LINUX only")
class TestProcess(unittest.TestCase):
def setUp(self):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
tearDown = setUp
@@ -781,73 +1008,42 @@ class TestProcess(unittest.TestCase):
# For all those cases we check that the value found in
# /proc/pid/stat (by psutil) matches the one found in
# /proc/pid/status.
- for p in psutil.process_iter():
- try:
- f = psutil._psplatform.open_text('/proc/%s/status' % p.pid)
- except IOError:
- pass
- else:
- with f:
- for line in f:
- line = line.strip()
- if line.startswith('Name:'):
- name = line.split()[1]
- # Name is truncated to 15 chars
- self.assertEqual(p.name()[:15], name[:15])
- elif line.startswith('State:'):
- status = line[line.find('(') + 1:line.rfind(')')]
- status = status.replace(' ', '-')
- self.assertEqual(p.status(), status)
- elif line.startswith('PPid:'):
- ppid = int(line.split()[1])
- self.assertEqual(p.ppid(), ppid)
- # The ones below internally are determined by reading
- # 'status' file but we use a re to extract the info
- # so it makes sense to check them.
- elif line.startswith('Threads:'):
- num_threads = int(line.split()[1])
- self.assertEqual(p.num_threads(), num_threads)
- elif line.startswith('Uid:'):
- uids = tuple(map(int, line.split()[1:4]))
- self.assertEqual(tuple(p.uids()), uids)
- elif line.startswith('Gid:'):
- gids = tuple(map(int, line.split()[1:4]))
- self.assertEqual(tuple(p.gids()), gids)
- elif line.startswith('voluntary_ctxt_switches:'):
- vol = int(line.split()[1])
- self.assertAlmostEqual(
- p.num_ctx_switches().voluntary, vol, delta=2)
- elif line.startswith('nonvoluntary_ctxt_switches:'):
- invol = int(line.split()[1])
- self.assertAlmostEqual(
- p.num_ctx_switches().involuntary, invol,
- delta=2)
-
- def test_memory_maps(self):
- src = textwrap.dedent("""
- import time
- with open("%s", "w") as f:
- time.sleep(10)
- """ % TESTFN)
- sproc = pyrun(src)
- self.addCleanup(reap_children)
- call_until(lambda: os.listdir('.'), "'%s' not in ret" % TESTFN)
- p = psutil.Process(sproc.pid)
- time.sleep(.1)
- maps = p.memory_maps(grouped=False)
- pmap = sh('pmap -x %s' % p.pid).split('\n')
- # get rid of header
- del pmap[0]
- del pmap[0]
- while maps and pmap:
- this = maps.pop(0)
- other = pmap.pop(0)
- addr, _, rss, dirty, mode, path = other.split(None, 5)
- if not path.startswith('[') and not path.endswith(']'):
- self.assertEqual(path, os.path.basename(this.path))
- self.assertEqual(int(rss) * 1024, this.rss)
- # test only rwx chars, ignore 's' and 'p'
- self.assertEqual(mode[:3], this.perms[:3])
+ p = psutil.Process()
+ with psutil._psplatform.open_text('/proc/%s/status' % p.pid) as f:
+ for line in f:
+ line = line.strip()
+ if line.startswith('Name:'):
+ name = line.split()[1]
+ # Name is truncated to 15 chars
+ self.assertEqual(p.name()[:15], name[:15])
+ elif line.startswith('State:'):
+ status = line[line.find('(') + 1:line.rfind(')')]
+ status = status.replace(' ', '-')
+ self.assertEqual(p.status(), status)
+ elif line.startswith('PPid:'):
+ ppid = int(line.split()[1])
+ self.assertEqual(p.ppid(), ppid)
+ # The ones below internally are determined by reading
+ # 'status' file but we use a re to extract the info
+ # so it makes sense to check them.
+ elif line.startswith('Threads:'):
+ num_threads = int(line.split()[1])
+ self.assertEqual(p.num_threads(), num_threads)
+ elif line.startswith('Uid:'):
+ uids = tuple(map(int, line.split()[1:4]))
+ self.assertEqual(tuple(p.uids()), uids)
+ elif line.startswith('Gid:'):
+ gids = tuple(map(int, line.split()[1:4]))
+ self.assertEqual(tuple(p.gids()), gids)
+ elif line.startswith('voluntary_ctxt_switches:'):
+ vol = int(line.split()[1])
+ self.assertAlmostEqual(
+ p.num_ctx_switches().voluntary, vol, delta=2)
+ elif line.startswith('nonvoluntary_ctxt_switches:'):
+ invol = int(line.split()[1])
+ self.assertAlmostEqual(
+ p.num_ctx_switches().involuntary, invol,
+ delta=2)
def test_memory_full_info(self):
src = textwrap.dedent("""
@@ -871,7 +1067,7 @@ class TestProcess(unittest.TestCase):
mem.swap, sum([x.swap for x in maps]), delta=4096)
# On PYPY file descriptors are not closed fast enough.
- @unittest.skipIf(PYPY, "skipped on PYPY")
+ @unittest.skipIf(PYPY, "unreliable on PYPY")
def test_open_files_mode(self):
def get_test_file():
p = psutil.Process()
@@ -900,10 +1096,10 @@ class TestProcess(unittest.TestCase):
self.assertEqual(get_test_file().mode, "a+")
# note: "x" bit is not supported
if PY3:
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
with open(TESTFN, "x"):
self.assertEqual(get_test_file().mode, "w")
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
with open(TESTFN, "x+"):
self.assertEqual(get_test_file().mode, "r+")
@@ -1001,7 +1197,7 @@ class TestProcess(unittest.TestCase):
# not sure why (doesn't fail locally)
# https://travis-ci.org/giampaolo/psutil/jobs/108629915
- @unittest.skipIf(TRAVIS, "fails on travis")
+ @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
def test_exe_mocked(self):
with mock.patch('psutil._pslinux.os.readlink',
side_effect=OSError(errno.ENOENT, "")) as m:
diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py
index b7eabffb..e9cf02df 100644..100755
--- a/psutil/tests/test_memory_leaks.py
+++ b/psutil/tests/test_memory_leaks.py
@@ -33,7 +33,7 @@ from psutil.tests import get_test_subprocess
from psutil.tests import reap_children
from psutil.tests import RLIMIT_SUPPORT
from psutil.tests import run_test_module_by_name
-from psutil.tests import safe_remove
+from psutil.tests import safe_rmpath
from psutil.tests import TESTFN
from psutil.tests import TRAVIS
from psutil.tests import unittest
@@ -46,7 +46,7 @@ SKIP_PYTHON_IMPL = True
def skip_if_linux():
return unittest.skipIf(LINUX and SKIP_PYTHON_IMPL,
- "not worth being tested on LINUX (pure python)")
+ "worthless on LINUX (pure python)")
class Base(unittest.TestCase):
@@ -171,12 +171,12 @@ class TestProcessObjectLeaks(Base):
self.execute('nice', niceness)
@unittest.skipUnless(hasattr(psutil.Process, 'ionice'),
- "Linux and Windows Vista only")
+ "platform not supported")
def test_ionice_get(self):
self.execute('ionice')
@unittest.skipUnless(hasattr(psutil.Process, 'ionice'),
- "Linux and Windows Vista only")
+ "platform not supported")
def test_ionice_set(self):
if WINDOWS:
value = psutil.Process().ionice()
@@ -187,12 +187,12 @@ class TestProcessObjectLeaks(Base):
fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0)
self.execute_w_exc(OSError, fun)
- @unittest.skipIf(OSX or SUNOS, "feature not supported on this platform")
+ @unittest.skipIf(OSX or SUNOS, "platform not supported")
@skip_if_linux()
def test_io_counters(self):
self.execute('io_counters')
- @unittest.skipUnless(WINDOWS, "not worth being tested on posix")
+ @unittest.skipIf(POSIX, "worthless on POSIX")
def test_username(self):
self.execute('username')
@@ -204,7 +204,7 @@ class TestProcessObjectLeaks(Base):
def test_num_threads(self):
self.execute('num_threads')
- @unittest.skipUnless(WINDOWS, "Windows only")
+ @unittest.skipUnless(WINDOWS, "WINDOWS only")
def test_num_handles(self):
self.execute('num_handles')
@@ -227,7 +227,7 @@ class TestProcessObjectLeaks(Base):
# also available on Linux but it's pure python
@unittest.skipUnless(OSX or WINDOWS,
- "not available on this platform")
+ "platform not supported")
def test_memory_full_info(self):
self.execute('memory_full_info')
@@ -237,7 +237,7 @@ class TestProcessObjectLeaks(Base):
self.execute('terminal')
@unittest.skipIf(POSIX and SKIP_PYTHON_IMPL,
- "not worth being tested on POSIX (pure python)")
+ "worthless on POSIX (pure python)")
def test_resume(self):
self.execute('resume')
@@ -246,12 +246,12 @@ class TestProcessObjectLeaks(Base):
self.execute('cwd')
@unittest.skipUnless(WINDOWS or LINUX or FREEBSD,
- "Windows or Linux or BSD only")
+ "platform not supported")
def test_cpu_affinity_get(self):
self.execute('cpu_affinity')
@unittest.skipUnless(WINDOWS or LINUX or FREEBSD,
- "Windows or Linux or BSD only")
+ "platform not supported")
def test_cpu_affinity_set(self):
affinity = psutil.Process().cpu_affinity()
self.execute('cpu_affinity', affinity)
@@ -260,34 +260,33 @@ class TestProcessObjectLeaks(Base):
@skip_if_linux()
def test_open_files(self):
- safe_remove(TESTFN) # needed after UNIX socket test has run
+ safe_rmpath(TESTFN) # needed after UNIX socket test has run
with open(TESTFN, 'w'):
self.execute('open_files')
# OSX implementation is unbelievably slow
- @unittest.skipIf(OSX, "OSX implementation is too slow")
- @unittest.skipIf(OPENBSD, "not implemented on OpenBSD")
+ @unittest.skipIf(OSX, "too slow on OSX")
+ @unittest.skipIf(OPENBSD, "platform not supported")
@skip_if_linux()
def test_memory_maps(self):
self.execute('memory_maps')
- @unittest.skipUnless(LINUX, "Linux only")
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX, "LINUX only")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_get(self):
self.execute('rlimit', psutil.RLIMIT_NOFILE)
- @unittest.skipUnless(LINUX, "Linux only")
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX, "LINUX only")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_set(self):
limit = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)
self.execute('rlimit', psutil.RLIMIT_NOFILE, limit)
self.execute_w_exc(OSError, 'rlimit', -1)
@skip_if_linux()
- # Windows implementation is based on a single system-wide function
- @unittest.skipIf(WINDOWS, "tested later")
+ # Windows implementation is based on a single system-wide
+ # function (tested later).
+ @unittest.skipIf(WINDOWS, "worthless on WINDOWS")
def test_connections(self):
def create_socket(family, type):
sock = socket.socket(family, type)
@@ -303,7 +302,7 @@ class TestProcessObjectLeaks(Base):
socks.append(create_socket(socket.AF_INET6, socket.SOCK_STREAM))
socks.append(create_socket(socket.AF_INET6, socket.SOCK_DGRAM))
if hasattr(socket, 'AF_UNIX'):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.bind(TESTFN)
s.listen(1)
@@ -321,7 +320,7 @@ class TestProcessObjectLeaks(Base):
s.close()
@unittest.skipUnless(hasattr(psutil.Process, 'environ'),
- "Linux, OSX and Windows")
+ "platform not supported")
def test_environ(self):
self.execute("environ")
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index 4357acc7..025bdba4 100644..100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -4,6 +4,10 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+"""
+Miscellaneous tests.
+"""
+
import ast
import errno
import imp
@@ -25,21 +29,25 @@ from psutil._common import memoize
from psutil._common import memoize_when_activated
from psutil._common import supports_ipv6
from psutil.tests import APPVEYOR
-from psutil.tests import SCRIPTS_DIR
+from psutil.tests import chdir
+from psutil.tests import get_test_subprocess
from psutil.tests import importlib
from psutil.tests import mock
+from psutil.tests import reap_children
+from psutil.tests import retry
from psutil.tests import ROOT_DIR
from psutil.tests import run_test_module_by_name
+from psutil.tests import safe_rmpath
+from psutil.tests import SCRIPTS_DIR
from psutil.tests import sh
+from psutil.tests import TESTFN
from psutil.tests import TOX
from psutil.tests import TRAVIS
from psutil.tests import unittest
+from psutil.tests import wait_for_file
+from psutil.tests import wait_for_pid
-# ===================================================================
-# --- Misc tests
-# ===================================================================
-
class TestMisc(unittest.TestCase):
"""Misc / generic tests."""
@@ -342,31 +350,26 @@ class TestMisc(unittest.TestCase):
psutil.Process()
assert meth.called
- def test_psutil_is_reloadable(self):
- importlib.reload(psutil)
-
def test_sanity_version_check(self):
# see: https://github.com/giampaolo/psutil/issues/564
- try:
- with mock.patch(
- "psutil._psplatform.cext.version", return_value="0.0.0"):
- with self.assertRaises(ImportError) as cm:
- importlib.reload(psutil)
- self.assertIn("version conflict", str(cm.exception).lower())
- finally:
- importlib.reload(psutil)
+ with mock.patch(
+ "psutil._psplatform.cext.version", return_value="0.0.0"):
+ with self.assertRaises(ImportError) as cm:
+ importlib.reload(psutil)
+ self.assertIn("version conflict", str(cm.exception).lower())
# ===================================================================
# --- Example script tests
# ===================================================================
-@unittest.skipIf(TOX, "can't test on tox")
+
+@unittest.skipIf(TOX, "can't test on TOX")
class TestScripts(unittest.TestCase):
"""Tests for scripts in the "scripts" directory."""
def assert_stdout(self, exe, args=None):
- exe = os.path.join(SCRIPTS_DIR, exe)
+ exe = '"%s"' % os.path.join(SCRIPTS_DIR, exe)
if args:
exe = exe + ' ' + args
try:
@@ -395,7 +398,7 @@ class TestScripts(unittest.TestCase):
self.fail('no test defined for %r script'
% os.path.join(SCRIPTS_DIR, name))
- @unittest.skipUnless(POSIX, "UNIX only")
+ @unittest.skipUnless(POSIX, "POSIX only")
def test_executable(self):
for name in os.listdir(SCRIPTS_DIR):
if name.endswith('.py'):
@@ -413,9 +416,10 @@ class TestScripts(unittest.TestCase):
self.assert_stdout('meminfo.py')
def test_procinfo(self):
- self.assert_stdout('procinfo.py')
+ self.assert_stdout('procinfo.py', args=str(os.getpid()))
- @unittest.skipIf(APPVEYOR, "can't find users on Appveyor")
+ # can't find users on APPVEYOR
+ @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
def test_who(self):
self.assert_stdout('who.py')
@@ -428,46 +432,188 @@ class TestScripts(unittest.TestCase):
def test_netstat(self):
self.assert_stdout('netstat.py')
- @unittest.skipIf(TRAVIS, "permission denied on travis")
+ # permission denied on travis
+ @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
def test_ifconfig(self):
self.assert_stdout('ifconfig.py')
- @unittest.skipIf(OPENBSD or NETBSD, "memory maps not supported")
+ @unittest.skipIf(OPENBSD or NETBSD, "platform not supported")
def test_pmap(self):
self.assert_stdout('pmap.py', args=str(os.getpid()))
- @unittest.skipUnless(OSX or WINDOWS or LINUX, "uss not available")
+ @unittest.skipUnless(OSX or WINDOWS or LINUX, "platform not supported")
def test_procsmem(self):
self.assert_stdout('procsmem.py')
- @unittest.skipIf(ast is None,
- 'ast module not available on this python version')
def test_killall(self):
self.assert_syntax('killall.py')
- @unittest.skipIf(ast is None,
- 'ast module not available on this python version')
def test_nettop(self):
self.assert_syntax('nettop.py')
- @unittest.skipIf(ast is None,
- 'ast module not available on this python version')
def test_top(self):
self.assert_syntax('top.py')
- @unittest.skipIf(ast is None,
- 'ast module not available on this python version')
def test_iotop(self):
self.assert_syntax('iotop.py')
def test_pidof(self):
- output = self.assert_stdout('pidof.py %s' % psutil.Process().name())
+ output = self.assert_stdout('pidof.py', args=psutil.Process().name())
self.assertIn(str(os.getpid()), output)
- @unittest.skipUnless(WINDOWS, "Windows only")
+ @unittest.skipUnless(WINDOWS, "WINDOWS only")
def test_winservices(self):
self.assert_stdout('winservices.py')
+# ===================================================================
+# --- Unit tests for test utilities.
+# ===================================================================
+
+
+class TestRetryDecorator(unittest.TestCase):
+
+ @mock.patch('time.sleep')
+ def test_retry_success(self, sleep):
+ # Fail 3 times out of 5; make sure the decorated fun returns.
+
+ @retry(retries=5, interval=1, logfun=None)
+ def foo():
+ while queue:
+ queue.pop()
+ 1 / 0
+ return 1
+
+ queue = list(range(3))
+ self.assertEqual(foo(), 1)
+ self.assertEqual(sleep.call_count, 3)
+
+ @mock.patch('time.sleep')
+ def test_retry_failure(self, sleep):
+ # Fail 6 times out of 5; th function is supposed to raise exc.
+
+ @retry(retries=5, interval=1, logfun=None)
+ def foo():
+ while queue:
+ queue.pop()
+ 1 / 0
+ return 1
+
+ queue = list(range(6))
+ self.assertRaises(ZeroDivisionError, foo)
+ self.assertEqual(sleep.call_count, 5)
+
+ @mock.patch('time.sleep')
+ def test_exception_arg(self, sleep):
+ @retry(exception=ValueError, interval=1)
+ def foo():
+ raise TypeError
+
+ self.assertRaises(TypeError, foo)
+ self.assertEqual(sleep.call_count, 0)
+
+ @mock.patch('time.sleep')
+ def test_no_interval_arg(self, sleep):
+ # if interval is not specified sleep is not supposed to be called
+
+ @retry(retries=5, interval=None, logfun=None)
+ def foo():
+ 1 / 0
+
+ self.assertRaises(ZeroDivisionError, foo)
+ self.assertEqual(sleep.call_count, 0)
+
+ @mock.patch('time.sleep')
+ def test_retries_arg(self, sleep):
+
+ @retry(retries=5, interval=1, logfun=None)
+ def foo():
+ 1 / 0
+
+ self.assertRaises(ZeroDivisionError, foo)
+ self.assertEqual(sleep.call_count, 5)
+
+ @mock.patch('time.sleep')
+ def test_retries_and_timeout_args(self, sleep):
+ self.assertRaises(ValueError, retry, retries=5, timeout=1)
+
+
+class TestSyncTestUtils(unittest.TestCase):
+
+ def tearDown(self):
+ safe_rmpath(TESTFN)
+
+ def test_wait_for_pid(self):
+ wait_for_pid(os.getpid())
+ nopid = max(psutil.pids()) + 99999
+ with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
+ self.assertRaises(psutil.NoSuchProcess, wait_for_pid, nopid)
+
+ def test_wait_for_file(self):
+ with open(TESTFN, 'w') as f:
+ f.write('foo')
+ wait_for_file(TESTFN)
+ assert not os.path.exists(TESTFN)
+
+ def test_wait_for_file_empty(self):
+ with open(TESTFN, 'w'):
+ pass
+ wait_for_file(TESTFN, empty=True)
+ assert not os.path.exists(TESTFN)
+
+ def test_wait_for_file_no_file(self):
+ with mock.patch('psutil.tests.retry.__iter__', return_value=iter([0])):
+ self.assertRaises(IOError, wait_for_file, TESTFN)
+
+ def test_wait_for_file_no_delete(self):
+ with open(TESTFN, 'w') as f:
+ f.write('foo')
+ wait_for_file(TESTFN, delete_file=False)
+ assert os.path.exists(TESTFN)
+
+
+class TestFSTestUtils(unittest.TestCase):
+
+ def setUp(self):
+ safe_rmpath(TESTFN)
+
+ tearDown = setUp
+
+ def test_safe_rmpath(self):
+ # test file is removed
+ open(TESTFN, 'w').close()
+ safe_rmpath(TESTFN)
+ assert not os.path.exists(TESTFN)
+ # test no exception if path does not exist
+ safe_rmpath(TESTFN)
+ # test dir is removed
+ os.mkdir(TESTFN)
+ safe_rmpath(TESTFN)
+ assert not os.path.exists(TESTFN)
+ # test other exceptions are raised
+ with mock.patch('psutil.tests.os.stat',
+ side_effect=OSError(errno.EINVAL, "")) as m:
+ with self.assertRaises(OSError):
+ safe_rmpath(TESTFN)
+ assert m.called
+
+ def test_chdir(self):
+ base = os.getcwd()
+ os.mkdir(TESTFN)
+ with chdir(TESTFN):
+ self.assertEqual(os.getcwd(), os.path.join(base, TESTFN))
+ self.assertEqual(os.getcwd(), base)
+
+
+class TestTestUtils(unittest.TestCase):
+
+ def test_reap_children(self):
+ subp = get_test_subprocess()
+ p = psutil.Process(subp.pid)
+ assert p.is_running()
+ reap_children()
+ assert not p.is_running()
+
+
if __name__ == '__main__':
run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py
index 3f308297..7b61bc74 100644..100755
--- a/psutil/tests/test_osx.py
+++ b/psutil/tests/test_osx.py
@@ -21,7 +21,6 @@ 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 TRAVIS
from psutil.tests import unittest
@@ -80,7 +79,7 @@ def human2bytes(s):
return int(num * prefix[letter])
-@unittest.skipUnless(OSX, "not an OSX system")
+@unittest.skipUnless(OSX, "OSX only")
class TestProcess(unittest.TestCase):
@classmethod
@@ -98,13 +97,18 @@ class TestProcess(unittest.TestCase):
if PY3:
output = str(output, sys.stdout.encoding)
start_ps = output.replace('STARTED', '').strip()
+ hhmmss = start_ps.split(' ')[-2]
+ year = start_ps.split(' ')[-1]
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)
+ self.assertEqual(
+ hhmmss,
+ time.strftime("%H:%M:%S", time.localtime(start_psutil)))
+ self.assertEqual(
+ year,
+ time.strftime("%Y", time.localtime(start_psutil)))
-@unittest.skipUnless(OSX, "not an OSX system")
+@unittest.skipUnless(OSX, "OSX only")
class TestSystemAPIs(unittest.TestCase):
def test_disks(self):
@@ -148,51 +152,70 @@ class TestSystemAPIs(unittest.TestCase):
sysctl_hwphymem = sysctl('sysctl hw.memsize')
self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total)
- @unittest.skipIf(TRAVIS, "")
@retry_before_failing()
def test_vmem_free(self):
- num = vm_stat("free")
- self.assertAlmostEqual(psutil.virtual_memory().free, num,
- delta=MEMORY_TOLERANCE)
+ 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):
- num = vm_stat("active")
- self.assertAlmostEqual(psutil.virtual_memory().active, num,
- delta=MEMORY_TOLERANCE)
+ 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):
- num = vm_stat("inactive")
- self.assertAlmostEqual(psutil.virtual_memory().inactive, num,
- delta=MEMORY_TOLERANCE)
+ 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):
- num = vm_stat("wired")
- self.assertAlmostEqual(psutil.virtual_memory().wired, num,
- delta=MEMORY_TOLERANCE)
+ 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):
- num = vm_stat("Pageins")
- self.assertEqual(psutil.swap_memory().sin, num)
+ 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):
- num = vm_stat("Pageouts")
- self.assertEqual(psutil.swap_memory().sout, num)
-
- 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))
+ 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))
+
+ 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('mtu (\d+)', out)[0]))
if __name__ == '__main__':
diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py
index 2645f70a..bb129a86 100644..100755
--- a/psutil/tests/test_posix.py
+++ b/psutil/tests/test_posix.py
@@ -57,7 +57,7 @@ def ps(cmd):
return output
-@unittest.skipUnless(POSIX, "not a POSIX system")
+@unittest.skipUnless(POSIX, "POSIX only")
class TestProcess(unittest.TestCase):
"""Compare psutil results against 'ps' command line utility (mainly)."""
@@ -122,8 +122,7 @@ class TestProcess(unittest.TestCase):
name_psutil = psutil.Process(self.pid).name().lower()
self.assertEqual(name_ps, name_psutil)
- @unittest.skipIf(OSX or BSD,
- 'ps -o start not available')
+ @unittest.skipIf(OSX 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()
@@ -212,7 +211,7 @@ class TestProcess(unittest.TestCase):
psutil.Process().cwd())
-@unittest.skipUnless(POSIX, "not a POSIX system")
+@unittest.skipUnless(POSIX, "POSIX only")
class TestSystemAPIs(unittest.TestCase):
"""Test some system APIs."""
@@ -251,11 +250,13 @@ class TestSystemAPIs(unittest.TestCase):
# for some reason ifconfig -a does not report all interfaces
# returned by psutil
- @unittest.skipIf(SUNOS, "test not reliable on SUNOS")
- @unittest.skipIf(TRAVIS, "test not reliable on Travis")
+ @unittest.skipIf(SUNOS, "unreliable on SUNOS")
+ @unittest.skipIf(TRAVIS, "unreliable on TRAVIS")
def test_nic_names(self):
p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE)
output = p.communicate()[0].strip()
+ if p.returncode != 0:
+ raise unittest.SkipTest('ifconfig returned no output')
if PY3:
output = str(output, sys.stdout.encoding)
for nic in psutil.net_io_counters(pernic=True).keys():
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index 3535d512..c3e9e290 100644..100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -12,7 +12,6 @@ import contextlib
import errno
import os
import select
-import shutil
import signal
import socket
import stat
@@ -26,10 +25,6 @@ import types
from socket import AF_INET
from socket import SOCK_DGRAM
from socket import SOCK_STREAM
-try:
- import ipaddress # python >= 3.3
-except ImportError:
- ipaddress = None
import psutil
@@ -53,9 +48,7 @@ from psutil.tests import APPVEYOR
from psutil.tests import call_until
from psutil.tests import chdir
from psutil.tests import check_connection_ntuple
-from psutil.tests import create_temp_executable_file
-from psutil.tests import decode_path
-from psutil.tests import encode_path
+from psutil.tests import create_exe
from psutil.tests import enum
from psutil.tests import get_test_subprocess
from psutil.tests import get_winver
@@ -68,8 +61,7 @@ from psutil.tests import reap_children
from psutil.tests import retry_before_failing
from psutil.tests import RLIMIT_SUPPORT
from psutil.tests import run_test_module_by_name
-from psutil.tests import safe_remove
-from psutil.tests import safe_rmdir
+from psutil.tests import safe_rmpath
from psutil.tests import sh
from psutil.tests import skip_on_access_denied
from psutil.tests import skip_on_not_implemented
@@ -83,7 +75,6 @@ from psutil.tests import VALID_PROC_STATUSES
from psutil.tests import wait_for_file
from psutil.tests import wait_for_pid
from psutil.tests import warn
-from psutil.tests import which
from psutil.tests import WIN_VISTA
@@ -95,7 +86,7 @@ class TestProcess(unittest.TestCase):
"""Tests for psutil.Process class."""
def setUp(self):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
def tearDown(self):
reap_children()
@@ -106,7 +97,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(psutil.Process(sproc.pid).pid, sproc.pid)
def test_kill(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
test_pid = sproc.pid
p = psutil.Process(test_pid)
p.kill()
@@ -116,7 +107,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(sig, signal.SIGKILL)
def test_terminate(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
test_pid = sproc.pid
p = psutil.Process(test_pid)
p.terminate()
@@ -139,19 +130,17 @@ class TestProcess(unittest.TestCase):
p = psutil.Process(sproc.pid)
p.send_signal(sig)
with mock.patch('psutil.os.kill',
- side_effect=OSError(errno.ESRCH, "")) as fun:
+ side_effect=OSError(errno.ESRCH, "")):
with self.assertRaises(psutil.NoSuchProcess):
p.send_signal(sig)
- assert fun.called
#
sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
p.send_signal(sig)
with mock.patch('psutil.os.kill',
- side_effect=OSError(errno.EPERM, "")) as fun:
+ side_effect=OSError(errno.EPERM, "")):
with self.assertRaises(psutil.AccessDenied):
psutil.Process().send_signal(sig)
- assert fun.called
# Sending a signal to process with PID 0 is not allowed as
# it would affect every process in the process group of
# the calling process (os.getpid()) instead of PID 0").
@@ -259,6 +248,8 @@ class TestProcess(unittest.TestCase):
self.assertLessEqual(percent, 100.0)
else:
self.assertGreaterEqual(percent, 0.0)
+ with self.assertRaises(ValueError):
+ p.cpu_percent(interval=-1)
def test_cpu_times(self):
times = psutil.Process().cpu_times()
@@ -276,7 +267,7 @@ class TestProcess(unittest.TestCase):
# try this with Python 2.7 and re-enable the test.
@unittest.skipUnless(sys.version_info > (2, 6, 1) and not OSX,
- 'os.times() is not reliable on this Python version')
+ 'os.times() broken on OSX + PY2.6.1')
def test_cpu_times_2(self):
user_time, kernel_time = psutil.Process().cpu_times()[:2]
utime, ktime = os.times()[:2]
@@ -291,7 +282,7 @@ class TestProcess(unittest.TestCase):
self.fail("expected: %s, found: %s" % (ktime, kernel_time))
def test_create_time(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
now = time.time()
p = psutil.Process(sproc.pid)
create_time = p.create_time()
@@ -307,17 +298,17 @@ class TestProcess(unittest.TestCase):
# make sure returned value can be pretty printed with strftime
time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time()))
- @unittest.skipIf(WINDOWS, 'UNIX only')
+ @unittest.skipUnless(POSIX, 'POSIX only')
def test_terminal(self):
terminal = psutil.Process().terminal()
if sys.stdin.isatty():
tty = os.path.realpath(sh('tty'))
self.assertEqual(terminal, tty)
else:
- assert terminal, repr(terminal)
+ self.assertIsNone(terminal)
@unittest.skipUnless(LINUX or BSD or WINDOWS,
- 'not available on this platform')
+ 'platform not supported')
@skip_on_not_implemented(only_if=LINUX)
def test_io_counters(self):
p = psutil.Process()
@@ -345,7 +336,7 @@ class TestProcess(unittest.TestCase):
assert io2.read_bytes >= io1.read_bytes, (io1, io2)
@unittest.skipUnless(LINUX or (WINDOWS and get_winver() >= WIN_VISTA),
- 'Linux and Windows Vista only')
+ 'platform not supported')
@unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
def test_ionice(self):
if LINUX:
@@ -409,8 +400,7 @@ class TestProcess(unittest.TestCase):
self.assertRaises(ValueError, p.ionice, 3)
self.assertRaises(TypeError, p.ionice, 2, 1)
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_get(self):
import resource
p = psutil.Process(os.getpid())
@@ -433,8 +423,7 @@ class TestProcess(unittest.TestCase):
self.assertGreaterEqual(ret[0], -1)
self.assertGreaterEqual(ret[1], -1)
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_set(self):
sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
@@ -447,8 +436,7 @@ class TestProcess(unittest.TestCase):
with self.assertRaises(ValueError):
p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5))
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit(self):
p = psutil.Process()
soft, hard = p.rlimit(psutil.RLIMIT_FSIZE)
@@ -467,8 +455,7 @@ class TestProcess(unittest.TestCase):
p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))
self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard))
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_infinity(self):
# First set a limit, then re-set it by specifying INFINITY
# and assume we overridden the previous limit.
@@ -483,8 +470,7 @@ class TestProcess(unittest.TestCase):
p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard))
self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard))
- @unittest.skipUnless(LINUX and RLIMIT_SUPPORT,
- "only available on Linux >= 2.6.36")
+ @unittest.skipUnless(LINUX and RLIMIT_SUPPORT, "LINUX >= 2.6.36 only")
def test_rlimit_infinity_value(self):
# RLIMIT_FSIZE should be RLIM_INFINITY, which will be a really
# big number on a platform with large file support. On these
@@ -519,7 +505,7 @@ class TestProcess(unittest.TestCase):
if thread._running:
thread.stop()
- @unittest.skipUnless(WINDOWS, 'Windows only')
+ @unittest.skipUnless(WINDOWS, 'WINDOWS only')
def test_num_handles(self):
# a better test is done later into test/_windows.py
p = psutil.Process()
@@ -557,19 +543,23 @@ class TestProcess(unittest.TestCase):
@retry_before_failing()
# see: https://travis-ci.org/giampaolo/psutil/jobs/111842553
- @unittest.skipIf(OSX and TRAVIS, "")
+ @unittest.skipIf(OSX and TRAVIS, "fails on TRAVIS + OSX")
+ @skip_on_access_denied(only_if=OSX)
def test_threads_2(self):
- p = psutil.Process()
+ sproc = get_test_subprocess()
+ p = psutil.Process(sproc.pid)
if OPENBSD:
try:
p.threads()
except psutil.AccessDenied:
raise unittest.SkipTest(
"on OpenBSD this requires root access")
- self.assertAlmostEqual(p.cpu_times().user,
- p.threads()[0].user_time, delta=0.1)
- self.assertAlmostEqual(p.cpu_times().system,
- p.threads()[0].system_time, delta=0.1)
+ self.assertAlmostEqual(
+ p.cpu_times().user,
+ sum([x.user_time for x in p.threads()]), delta=0.1)
+ self.assertAlmostEqual(
+ p.cpu_times().system,
+ sum([x.system_time for x in p.threads()]), delta=0.1)
def test_memory_info(self):
p = psutil.Process()
@@ -602,16 +592,19 @@ class TestProcess(unittest.TestCase):
self.assertGreaterEqual(getattr(mem, name), 0)
def test_memory_full_info(self):
+ total = psutil.virtual_memory().total
mem = psutil.Process().memory_full_info()
for name in mem._fields:
- self.assertGreaterEqual(getattr(mem, name), 0)
+ value = getattr(mem, name)
+ self.assertGreaterEqual(value, 0, msg=(name, value))
+ self.assertLessEqual(value, total, msg=(name, value, total))
if LINUX or WINDOWS or OSX:
- self.assertGreater(mem.uss, 0)
+ mem.uss
if LINUX:
- self.assertGreater(mem.pss, 0)
+ mem.pss
self.assertGreater(mem.pss, mem.uss)
- @unittest.skipIf(OPENBSD or NETBSD, "not available on this platform")
+ @unittest.skipIf(OPENBSD or NETBSD, "platfform not supported")
def test_memory_maps(self):
p = psutil.Process()
maps = p.memory_maps()
@@ -666,7 +659,7 @@ class TestProcess(unittest.TestCase):
assert 0 <= ret <= 100, ret
def test_is_running(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
assert p.is_running()
assert p.is_running()
@@ -676,7 +669,7 @@ class TestProcess(unittest.TestCase):
assert not p.is_running()
def test_exe(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
exe = psutil.Process(sproc.pid).exe()
try:
self.assertEqual(exe, PYTHON)
@@ -693,11 +686,21 @@ class TestProcess(unittest.TestCase):
# We do not want to consider this difference in accuracy
# an error.
ver = "%s.%s" % (sys.version_info[0], sys.version_info[1])
- self.assertEqual(exe.replace(ver, ''), PYTHON.replace(ver, ''))
+ try:
+ self.assertEqual(exe.replace(ver, ''),
+ PYTHON.replace(ver, ''))
+ except AssertionError:
+ # Tipically OSX. Really not sure what to do here.
+ pass
+
+ subp = subprocess.Popen([exe, '-c', 'import os; print("hey")'],
+ stdout=subprocess.PIPE)
+ out, _ = subp.communicate()
+ self.assertEqual(out.strip(), b'hey')
def test_cmdline(self):
cmdline = [PYTHON, "-c", "import time; time.sleep(60)"]
- sproc = get_test_subprocess(cmdline, wait=True)
+ sproc = get_test_subprocess(cmdline)
try:
self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()),
' '.join(cmdline))
@@ -713,18 +716,20 @@ class TestProcess(unittest.TestCase):
raise
def test_name(self):
- sproc = get_test_subprocess(PYTHON, wait=True)
+ sproc = get_test_subprocess(PYTHON)
name = psutil.Process(sproc.pid).name().lower()
pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
assert pyexe.startswith(name), (pyexe, name)
- @unittest.skipIf(SUNOS, "doesn't work on Solaris")
+ # XXX
+ @unittest.skipIf(SUNOS, "broken on SUNOS")
def test_prog_w_funky_name(self):
# Test that name(), exe() and cmdline() correctly handle programs
# with funky chars such as spaces and ")", see:
# https://github.com/giampaolo/psutil/issues/628
- funky_path = create_temp_executable_file('foo bar )')
- self.addCleanup(safe_remove, funky_path)
+ funky_path = TESTFN + 'foo bar )'
+ create_exe(funky_path)
+ self.addCleanup(safe_rmpath, funky_path)
cmdline = [funky_path, "-c",
"import time; [time.sleep(0.01) for x in range(3000)];"
"arg1", "arg2", "", "arg3", ""]
@@ -738,7 +743,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(os.path.normcase(p.exe()),
os.path.normcase(funky_path))
- @unittest.skipUnless(POSIX, 'posix only')
+ @unittest.skipUnless(POSIX, 'POSIX only')
def test_uids(self):
p = psutil.Process()
real, effective, saved = p.uids()
@@ -751,7 +756,7 @@ class TestProcess(unittest.TestCase):
if hasattr(os, "getresuid"):
self.assertEqual(saved, os.getresuid()[2])
- @unittest.skipUnless(POSIX, 'posix only')
+ @unittest.skipUnless(POSIX, 'POSIX only')
def test_gids(self):
p = psutil.Process()
real, effective, saved = p.gids()
@@ -759,7 +764,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(real, os.getgid())
# os.geteuid() refers to "effective" uid
self.assertEqual(effective, os.getegid())
- # no such thing as os.getsuid() ("saved" uid), but starting
+ # no such thing as os.getsgid() ("saved" gid), but starting
# from python 2.7 we have os.getresgid()[2]
if hasattr(os, "getresuid"):
self.assertEqual(saved, os.getresgid()[2])
@@ -824,19 +829,18 @@ class TestProcess(unittest.TestCase):
p.username()
def test_cwd(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
self.assertEqual(p.cwd(), os.getcwd())
def test_cwd_2(self):
cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(60)"]
- sproc = get_test_subprocess(cmd, wait=True)
+ sproc = get_test_subprocess(cmd)
p = psutil.Process(sproc.pid)
call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
- @unittest.skipUnless(WINDOWS or LINUX or FREEBSD,
- 'not available on this platform')
- @unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
+ @unittest.skipUnless(WINDOWS or LINUX or FREEBSD, 'platform not supported')
+ @unittest.skipIf(LINUX and TRAVIS, "unreliable on TRAVIS")
def test_cpu_affinity(self):
p = psutil.Process()
initial = p.cpu_affinity()
@@ -869,11 +873,12 @@ class TestProcess(unittest.TestCase):
self.assertRaises(ValueError, p.cpu_affinity, invalid_cpu)
self.assertRaises(ValueError, p.cpu_affinity, range(10000, 11000))
self.assertRaises(TypeError, p.cpu_affinity, [0, "1"])
+ self.assertRaises(ValueError, p.cpu_affinity, [0, -1])
- # TODO
- @unittest.skipIf(BSD, "broken on BSD, see #595")
- @unittest.skipIf(APPVEYOR,
- "can't find any process file on Appveyor")
+ # TODO: #595
+ @unittest.skipIf(BSD, "broken on BSD")
+ # can't find any process file on Appveyor
+ @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
def test_open_files(self):
# current process
p = psutil.Process()
@@ -896,7 +901,7 @@ class TestProcess(unittest.TestCase):
# another process
cmdline = "import time; f = open(r'%s', 'r'); time.sleep(60);" % TESTFN
- sproc = get_test_subprocess([PYTHON, "-c", cmdline], wait=True)
+ sproc = get_test_subprocess([PYTHON, "-c", cmdline])
p = psutil.Process(sproc.pid)
for x in range(100):
@@ -909,10 +914,10 @@ class TestProcess(unittest.TestCase):
for file in filenames:
assert os.path.isfile(file), file
- # TODO
- @unittest.skipIf(BSD, "broken on BSD, see #595")
- @unittest.skipIf(APPVEYOR,
- "can't find any process file on Appveyor")
+ # TODO: #595
+ @unittest.skipIf(BSD, "broken on BSD")
+ # can't find any process file on Appveyor
+ @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
def test_open_files_2(self):
# test fd and path fields
with open(TESTFN, 'w') as fileobj:
@@ -936,10 +941,8 @@ class TestProcess(unittest.TestCase):
def compare_proc_sys_cons(self, pid, proc_cons):
from psutil._common import pconn
- sys_cons = []
- for c in psutil.net_connections(kind='all'):
- if c.pid == pid:
- sys_cons.append(pconn(*c[:-1]))
+ sys_cons = [c[:-1] for c in psutil.net_connections(kind='all')
+ if c.pid == pid]
if FREEBSD:
# on FreeBSD all fds are set to -1
proc_cons = [pconn(*[-1] + list(x[1:])) for x in proc_cons]
@@ -1040,15 +1043,15 @@ class TestProcess(unittest.TestCase):
psutil.CONN_NONE,
("all", "inet", "inet6", "udp", "udp6"))
- @unittest.skipUnless(hasattr(socket, 'AF_UNIX'),
- 'AF_UNIX is not supported')
+ @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'AF_UNIX not supported')
@skip_on_access_denied(only_if=OSX)
def test_connections_unix(self):
def check(type):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
+ tfile = tempfile.mktemp(prefix=TESTFILE_PREFIX) if OSX else TESTFN
sock = socket.socket(AF_UNIX, type)
with contextlib.closing(sock):
- sock.bind(TESTFN)
+ sock.bind(tfile)
cons = psutil.Process().connections(kind='unix')
conn = cons[0]
check_connection_ntuple(conn)
@@ -1056,7 +1059,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(conn.fd, sock.fileno())
self.assertEqual(conn.family, AF_UNIX)
self.assertEqual(conn.type, type)
- self.assertEqual(conn.laddr, TESTFN)
+ self.assertEqual(conn.laddr, tfile)
if not SUNOS:
# XXX Solaris can't retrieve system-wide UNIX
# sockets.
@@ -1066,7 +1069,7 @@ class TestProcess(unittest.TestCase):
check(SOCK_DGRAM)
@unittest.skipUnless(hasattr(socket, "fromfd"),
- 'socket.fromfd() is not availble')
+ 'socket.fromfd() not supported')
@unittest.skipIf(WINDOWS or SUNOS,
'connection fd not available on this platform')
def test_connection_fromfd(self):
@@ -1102,7 +1105,7 @@ class TestProcess(unittest.TestCase):
if WINDOWS:
psutil.CONN_DELETE_TCB
- @unittest.skipUnless(POSIX, 'posix only')
+ @unittest.skipUnless(POSIX, 'POSIX only')
def test_num_fds(self):
p = psutil.Process()
start = p.num_fds()
@@ -1117,7 +1120,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.num_fds(), start)
@skip_on_not_implemented(only_if=LINUX)
- @unittest.skipIf(OPENBSD or NETBSD, "not reliable on Open/NetBSD")
+ @unittest.skipIf(OPENBSD or NETBSD, "not reliable on OPENBSD & NETBSD")
def test_num_ctx_switches(self):
p = psutil.Process()
before = sum(p.num_ctx_switches())
@@ -1192,7 +1195,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(len(c), len(set(c)))
def test_suspend_resume(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
p.suspend()
for x in range(100):
@@ -1297,7 +1300,7 @@ class TestProcess(unittest.TestCase):
"NoSuchProcess exception not raised for %r, retval=%s" % (
name, ret))
- @unittest.skipUnless(POSIX, 'posix only')
+ @unittest.skipUnless(POSIX, 'POSIX only')
def test_zombie_process(self):
def succeed_or_zombie_p_exc(fun, *args, **kwargs):
try:
@@ -1309,6 +1312,7 @@ class TestProcess(unittest.TestCase):
# Both of them are supposed to be freed / killed by
# reap_children() as they are attributable to 'us'
# (os.getpid()) via children(recursive=True).
+ unix_file = tempfile.mktemp(prefix=TESTFILE_PREFIX) if OSX else TESTFN
src = textwrap.dedent("""\
import os, sys, time, socket, contextlib
child_pid = os.fork()
@@ -1324,11 +1328,11 @@ class TestProcess(unittest.TestCase):
else:
pid = bytes(str(os.getpid()), 'ascii')
s.sendall(pid)
- """ % TESTFN)
+ """ % unix_file)
with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
try:
sock.settimeout(GLOBAL_TIMEOUT)
- sock.bind(TESTFN)
+ sock.bind(unix_file)
sock.listen(1)
pyrun(src)
conn, _ = sock.accept()
@@ -1393,46 +1397,36 @@ class TestProcess(unittest.TestCase):
def test_pid_0(self):
# Process(0) is supposed to work on all platforms except Linux
- if 0 not in psutil.pids() and not OPENBSD:
+ if 0 not in psutil.pids():
self.assertRaises(psutil.NoSuchProcess, psutil.Process, 0)
return
+ # test all methods
p = psutil.Process(0)
- self.assertTrue(p.name())
-
- if POSIX:
+ for name in psutil._as_dict_attrnames:
+ if name == 'pid':
+ continue
+ meth = getattr(p, name)
try:
- self.assertEqual(p.uids().real, 0)
- self.assertEqual(p.gids().real, 0)
+ ret = meth()
except psutil.AccessDenied:
pass
-
- self.assertRaisesRegex(
- ValueError, "preventing sending signal to process with PID 0",
- p.send_signal, signal.SIGTERM)
-
- self.assertIn(p.ppid(), (0, 1))
- # self.assertEqual(p.exe(), "")
- p.cmdline()
- try:
- p.num_threads()
- except psutil.AccessDenied:
- pass
-
- try:
- p.memory_info()
- except psutil.AccessDenied:
- pass
-
- try:
- if POSIX:
- self.assertEqual(p.username(), 'root')
- elif WINDOWS:
- self.assertEqual(p.username(), 'NT AUTHORITY\\SYSTEM')
else:
- p.username()
- except psutil.AccessDenied:
- pass
+ if name in ("uids", "gids"):
+ self.assertEqual(ret.real, 0)
+ elif name == "username":
+ if POSIX:
+ self.assertEqual(p.username(), 'root')
+ elif WINDOWS:
+ self.assertEqual(p.username(), 'NT AUTHORITY\\SYSTEM')
+ elif name == "name":
+ assert name, name
+
+ if hasattr(p, 'rlimit'):
+ try:
+ p.rlimit(psutil.RLIMIT_FSIZE)
+ except psutil.AccessDenied:
+ pass
p.as_dict()
@@ -1441,7 +1435,6 @@ class TestProcess(unittest.TestCase):
self.assertTrue(psutil.pid_exists(0))
def test_Popen(self):
- # Popen class test
# XXX this test causes a ResourceWarning on Python 3 because
# psutil.__subproc instance doesn't get propertly freed.
# Not sure what to do though.
@@ -1450,18 +1443,26 @@ class TestProcess(unittest.TestCase):
stderr=subprocess.PIPE)
try:
proc.name()
+ proc.cpu_times()
proc.stdin
- self.assertTrue(hasattr(proc, 'name'))
- self.assertTrue(hasattr(proc, 'stdin'))
self.assertTrue(dir(proc))
self.assertRaises(AttributeError, getattr, proc, 'foo')
finally:
proc.kill()
proc.wait()
- self.assertIsNotNone(proc.returncode)
+
+ def test_Popen_ctx_manager(self):
+ with psutil.Popen([PYTHON, "-V"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE) as proc:
+ pass
+ assert proc.stdout.closed
+ assert proc.stderr.closed
+ assert proc.stdin.closed
@unittest.skipUnless(hasattr(psutil.Process, "environ"),
- "environ not available")
+ "platform not supported")
def test_environ(self):
self.maxDiff = None
p = psutil.Process()
@@ -1485,7 +1486,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(d, d2)
@unittest.skipUnless(hasattr(psutil.Process, "environ"),
- "environ not available")
+ "platform not supported")
@unittest.skipUnless(POSIX, "posix only")
def test_weird_environ(self):
# environment variables can contain values without an equals sign
@@ -1502,8 +1503,9 @@ class TestProcess(unittest.TestCase):
return execve("/bin/cat", argv, envp);
}
""")
- path = create_temp_executable_file("x", c_code=code)
- self.addCleanup(safe_remove, path)
+ path = TESTFN
+ create_exe(path, c_code=code)
+ self.addCleanup(safe_rmpath, path)
sproc = get_test_subprocess([path],
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
@@ -1529,9 +1531,12 @@ class TestFetchAllProcesses(unittest.TestCase):
def setUp(self):
if POSIX:
import pwd
- pall = pwd.getpwall()
- self._uids = set([x.pw_uid for x in pall])
- self._usernames = set([x.pw_name for x in pall])
+ import grp
+ users = pwd.getpwall()
+ groups = grp.getgrall()
+ self.all_uids = set([x.pw_uid for x in users])
+ self.all_usernames = set([x.pw_name for x in users])
+ self.all_gids = set([x.gr_gid for x in groups])
def test_fetch_all(self):
valid_procs = 0
@@ -1642,20 +1647,21 @@ class TestFetchAllProcesses(unittest.TestCase):
def uids(self, ret, proc):
for uid in ret:
- self.assertTrue(uid >= 0)
- self.assertIn(uid, self._uids)
+ self.assertGreaterEqual(uid, 0)
+ self.assertIn(uid, self.all_uids)
def gids(self, ret, proc):
# note: testing all gids as above seems not to be reliable for
# gid == 30 (nodoby); not sure why.
for gid in ret:
- self.assertTrue(gid >= 0)
- # self.assertIn(uid, self.gids
+ if not OSX and not NETBSD:
+ self.assertGreaterEqual(gid, 0)
+ self.assertIn(gid, self.all_gids)
def username(self, ret, proc):
self.assertTrue(ret)
if POSIX:
- self.assertIn(ret, self._usernames)
+ self.assertIn(ret, self.all_usernames)
def status(self, ret, proc):
self.assertTrue(ret != "")
@@ -1704,8 +1710,12 @@ class TestFetchAllProcesses(unittest.TestCase):
assert ret.peak_pagefile >= ret.pagefile, ret
def memory_full_info(self, ret, proc):
+ total = psutil.virtual_memory().total
for name in ret._fields:
- self.assertGreaterEqual(getattr(ret, name), 0)
+ value = getattr(ret, name)
+ self.assertGreaterEqual(value, 0, msg=(name, value))
+ self.assertLessEqual(value, total, msg=(name, value, total))
+
if LINUX:
self.assertGreaterEqual(ret.pss, ret.uss)
@@ -1807,6 +1817,7 @@ class TestFetchAllProcesses(unittest.TestCase):
# --- Limited user tests
# ===================================================================
+
if POSIX and os.getuid() == 0:
class LimitedUserTestCase(TestProcess):
"""Repeat the previous tests by using a limited user.
@@ -1833,7 +1844,7 @@ if POSIX and os.getuid() == 0:
setattr(self, attr, types.MethodType(test_, self))
def setUp(self):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
TestProcess.setUp(self)
os.setegid(1000)
os.seteuid(1000)
@@ -1860,19 +1871,27 @@ if POSIX and os.getuid() == 0:
# --- Unicode tests
# ===================================================================
+
class TestUnicode(unittest.TestCase):
- # See: https://github.com/giampaolo/psutil/issues/655
+ """
+ Make sure that APIs returning a string are able to handle unicode,
+ see: https://github.com/giampaolo/psutil/issues/655
+ """
+ uexe = TESTFN + 'èfile'
+ udir = TESTFN + 'èdir'
@classmethod
def setUpClass(cls):
- cls.uexe = create_temp_executable_file('è')
- cls.ubasename = os.path.basename(cls.uexe)
- assert 'è' in cls.ubasename
+ safe_rmpath(cls.uexe)
+ safe_rmpath(cls.udir)
+ create_exe(cls.uexe)
+ os.mkdir(cls.udir)
@classmethod
def tearDownClass(cls):
if not APPVEYOR:
- safe_remove(cls.uexe)
+ safe_rmpath(cls.uexe)
+ safe_rmpath(cls.udir)
def setUp(self):
reap_children()
@@ -1883,32 +1902,41 @@ class TestUnicode(unittest.TestCase):
subp = get_test_subprocess(cmd=[self.uexe])
p = psutil.Process(subp.pid)
self.assertIsInstance(p.name(), str)
- self.assertEqual(os.path.basename(p.name()), self.ubasename)
+ if not OSX and TRAVIS:
+ self.assertEqual(p.exe(), self.uexe)
+ else:
+ p.exe()
def test_proc_name(self):
subp = get_test_subprocess(cmd=[self.uexe])
if WINDOWS:
+ # XXX: why is this like this?
from psutil._pswindows import py2_strencode
name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid))
else:
name = psutil.Process(subp.pid).name()
- self.assertEqual(name, self.ubasename)
+ if not OSX and TRAVIS:
+ self.assertEqual(name, os.path.basename(self.uexe))
def test_proc_cmdline(self):
subp = get_test_subprocess(cmd=[self.uexe])
p = psutil.Process(subp.pid)
self.assertIsInstance("".join(p.cmdline()), str)
- self.assertEqual(p.cmdline(), [self.uexe])
+ if not OSX and TRAVIS:
+ self.assertEqual(p.cmdline(), [self.uexe])
+ else:
+ p.cmdline()
def test_proc_cwd(self):
- tdir = os.path.realpath(tempfile.mkdtemp(prefix="psutil-è-"))
- self.addCleanup(safe_rmdir, tdir)
- with chdir(tdir):
+ with chdir(self.udir):
p = psutil.Process()
self.assertIsInstance(p.cwd(), str)
- self.assertEqual(p.cwd(), tdir)
+ if not OSX and TRAVIS:
+ self.assertEqual(p.cwd(), self.udir)
+ else:
+ p.cwd()
- @unittest.skipIf(APPVEYOR, "")
+ # @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR")
def test_proc_open_files(self):
p = psutil.Process()
start = set(p.open_files())
@@ -1920,10 +1948,12 @@ class TestUnicode(unittest.TestCase):
# see https://github.com/giampaolo/psutil/issues/595
self.skipTest("open_files on BSD is broken")
self.assertIsInstance(path, str)
- self.assertEqual(os.path.normcase(path), os.path.normcase(self.uexe))
+ if not OSX and TRAVIS:
+ self.assertEqual(os.path.normcase(path),
+ os.path.normcase(self.uexe))
@unittest.skipUnless(hasattr(psutil.Process, "environ"),
- "environ not available")
+ "platform not supported")
def test_proc_environ(self):
env = os.environ.copy()
env['FUNNY_ARG'] = self.uexe
@@ -1933,137 +1963,25 @@ class TestUnicode(unittest.TestCase):
uexe = self.uexe.decode(sys.getfilesystemencoding())
else:
uexe = self.uexe
- self.assertEqual(p.environ()['FUNNY_ARG'], uexe)
-
- def test_disk_usage(self):
- path = tempfile.mkdtemp(prefix='psutil', suffix='è')
- psutil.disk_usage(path)
-
-
-class TestNonUnicode(unittest.TestCase):
- """Test handling of non-utf8 data."""
-
- @classmethod
- def setUpClass(cls):
- if PY3:
- # Fix around https://bugs.python.org/issue24230
- cls.temp_directory = tempfile.mkdtemp().encode('utf8')
+ if not OSX and TRAVIS:
+ self.assertEqual(p.environ()['FUNNY_ARG'], uexe)
else:
- cls.temp_directory = tempfile.mkdtemp(suffix=b"")
-
- # Return an executable that runs until we close its stdin.
- if WINDOWS:
- cls.test_executable = which("cmd.exe")
- else:
- cls.test_executable = which("cat")
-
- @classmethod
- def tearDownClass(cls):
- shutil.rmtree(cls.temp_directory, ignore_errors=True)
-
- def setUp(self):
- reap_children()
-
- tearDown = setUp
-
- def copy_file(self, src, dst):
- # A wrapper around shutil.copy() which is broken on py < 3.4
- # when passed bytes paths.
- with open(src, 'rb') as input_:
- with open(dst, 'wb') as output:
- output.write(input_.read())
- shutil.copymode(src, dst)
-
- def test_proc_exe(self):
- funny_executable = os.path.join(self.temp_directory, b"\xc0\x80")
- self.copy_file(self.test_executable, funny_executable)
- self.addCleanup(safe_remove, funny_executable)
- subp = get_test_subprocess(cmd=[decode_path(funny_executable)],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- p = psutil.Process(subp.pid)
- self.assertIsInstance(p.exe(), str)
- self.assertEqual(encode_path(os.path.basename(p.exe())), b"\xc0\x80")
- subp.communicate()
- self.assertEqual(subp.returncode, 0)
-
- def test_proc_name(self):
- funny_executable = os.path.join(self.temp_directory, b"\xc0\x80")
- self.copy_file(self.test_executable, funny_executable)
- self.addCleanup(safe_remove, funny_executable)
- subp = get_test_subprocess(cmd=[decode_path(funny_executable)],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- p = psutil.Process(subp.pid)
- self.assertEqual(encode_path(os.path.basename(p.name())), b"\xc0\x80")
- subp.communicate()
- self.assertEqual(subp.returncode, 0)
-
- def test_proc_cmdline(self):
- funny_executable = os.path.join(self.temp_directory, b"\xc0\x80")
- self.copy_file(self.test_executable, funny_executable)
- self.addCleanup(safe_remove, funny_executable)
- subp = get_test_subprocess(cmd=[decode_path(funny_executable)],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- p = psutil.Process(subp.pid)
- self.assertEqual(p.cmdline(), [decode_path(funny_executable)])
- subp.communicate()
- self.assertEqual(subp.returncode, 0)
-
- def test_proc_cwd(self):
- funny_directory = os.path.realpath(
- os.path.join(self.temp_directory, b"\xc0\x80"))
- os.mkdir(funny_directory)
- self.addCleanup(safe_rmdir, funny_directory)
- with chdir(funny_directory):
- p = psutil.Process()
- self.assertIsInstance(p.cwd(), str)
- self.assertEqual(encode_path(p.cwd()), funny_directory)
-
- @unittest.skipIf(WINDOWS, "does not work on windows")
- def test_proc_open_files(self):
- funny_file = os.path.join(self.temp_directory, b"\xc0\x80")
- p = psutil.Process()
- start = set(p.open_files())
- with open(funny_file, 'wb'):
- new = set(p.open_files())
- path = (new - start).pop().path
- if BSD and not path:
- # XXX
- # see https://github.com/giampaolo/psutil/issues/595
- self.skipTest("open_files on BSD is broken")
- self.assertIsInstance(path, str)
- self.assertIn(funny_file, encode_path(path))
-
- @unittest.skipUnless(hasattr(psutil.Process, "environ"),
- "environ not available")
- def test_proc_environ(self):
- env = os.environ.copy()
- funny_path = self.temp_directory
- # ...otherwise subprocess.Popen fails with TypeError (it
- # wants a string)
- env['FUNNY_ARG'] = \
- decode_path(funny_path) if WINDOWS and PY3 else funny_path
- sproc = get_test_subprocess(env=env)
- p = psutil.Process(sproc.pid)
- self.assertEqual(
- encode_path(p.environ()['FUNNY_ARG']), funny_path)
+ p.environ()
def test_disk_usage(self):
- funny_directory = os.path.realpath(
- os.path.join(self.temp_directory, b"\xc0\x80"))
- os.mkdir(funny_directory)
- self.addCleanup(safe_rmdir, funny_directory)
- if WINDOWS and PY3:
- # Python 3 on Windows is moving towards accepting unicode
- # paths only:
- # http://bugs.python.org/issue26330
- funny_directory = decode_path(funny_directory)
- psutil.disk_usage(funny_directory)
+ psutil.disk_usage(self.udir)
+
+
+class TestInvalidUnicode(TestUnicode):
+ """Test handling of invalid utf8 data."""
+ if PY3:
+ uexe = (TESTFN.encode('utf8') + b"f\xc0\x80").decode(
+ 'utf8', 'surrogateescape')
+ udir = (TESTFN.encode('utf8') + b"d\xc0\x80").decode(
+ 'utf8', 'surrogateescape')
+ else:
+ uexe = TESTFN + b"f\xc0\x80"
+ udir = TESTFN + b"d\xc0\x80"
if __name__ == '__main__':
diff --git a/psutil/tests/test_sunos.py b/psutil/tests/test_sunos.py
index 2afc8776..9694b22b 100644..100755
--- a/psutil/tests/test_sunos.py
+++ b/psutil/tests/test_sunos.py
@@ -15,7 +15,7 @@ from psutil.tests import sh
from psutil.tests import unittest
-@unittest.skipUnless(SUNOS, "not a SunOS system")
+@unittest.skipUnless(SUNOS, "SUNOS only")
class SunOSSpecificTestCase(unittest.TestCase):
def test_swap_memory(self):
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index f5c9abd6..57a447ad 100644..100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -39,8 +39,7 @@ 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_remove
-from psutil.tests import safe_rmdir
+from psutil.tests import safe_rmpath
from psutil.tests import skip_on_access_denied
from psutil.tests import TESTFN
from psutil.tests import TESTFN_UNICODE
@@ -57,7 +56,7 @@ class TestSystemAPIs(unittest.TestCase):
"""Tests for system-related APIs."""
def setUp(self):
- safe_remove(TESTFN)
+ safe_rmpath(TESTFN)
def tearDown(self):
reap_children()
@@ -149,7 +148,7 @@ class TestSystemAPIs(unittest.TestCase):
self.assertGreater(bt, 0)
self.assertLess(bt, time.time())
- @unittest.skipUnless(POSIX, 'posix only')
+ @unittest.skipUnless(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
@@ -189,7 +188,7 @@ class TestSystemAPIs(unittest.TestCase):
assert mem.sout >= 0, mem
def test_pid_exists(self):
- sproc = get_test_subprocess(wait=True)
+ sproc = get_test_subprocess()
self.assertTrue(psutil.pid_exists(sproc.pid))
p = psutil.Process(sproc.pid)
p.kill()
@@ -359,6 +358,8 @@ class TestSystemAPIs(unittest.TestCase):
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)
@@ -368,6 +369,8 @@ class TestSystemAPIs(unittest.TestCase):
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)
@@ -400,7 +403,7 @@ class TestSystemAPIs(unittest.TestCase):
self._test_cpu_percent(percent, None, None)
@unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
- "os.statvfs() function not available on this platform")
+ "os.statvfs() not available")
def test_disk_usage(self):
usage = psutil.disk_usage(os.getcwd())
assert usage.total > 0, usage
@@ -431,16 +434,16 @@ class TestSystemAPIs(unittest.TestCase):
self.fail("OSError not raised")
@unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
- "os.statvfs() function not available on this platform")
+ "os.statvfs() not available")
def test_disk_usage_unicode(self):
# see: https://github.com/giampaolo/psutil/issues/416
- safe_rmdir(TESTFN_UNICODE)
- self.addCleanup(safe_rmdir, TESTFN_UNICODE)
+ safe_rmpath(TESTFN_UNICODE)
+ self.addCleanup(safe_rmpath, TESTFN_UNICODE)
os.mkdir(TESTFN_UNICODE)
psutil.disk_usage(TESTFN_UNICODE)
@unittest.skipIf(POSIX and not hasattr(os, 'statvfs'),
- "os.statvfs() function not available on this platform")
+ "os.statvfs() not available")
@unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
def test_disk_partitions(self):
# all = False
@@ -493,10 +496,11 @@ class TestSystemAPIs(unittest.TestCase):
path = os.path.abspath(path)
while not os.path.ismount(path):
path = os.path.dirname(path)
- return path
+ return path.lower()
mount = find_mount_point(__file__)
- mounts = [x.mountpoint for x in psutil.disk_partitions(all=True)]
+ mounts = [x.mountpoint.lower() for x in
+ psutil.disk_partitions(all=True)]
self.assertIn(mount, mounts)
psutil.disk_usage(mount)
@@ -548,6 +552,8 @@ class TestSystemAPIs(unittest.TestCase):
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()),
@@ -564,18 +570,21 @@ class TestSystemAPIs(unittest.TestCase):
self.assertIn(addr.family, families)
if sys.version_info >= (3, 4):
self.assertIsInstance(addr.family, enum.IntEnum)
- 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)
+ 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:
@@ -615,7 +624,7 @@ class TestSystemAPIs(unittest.TestCase):
else:
self.assertEqual(addr.address, '06-3d-29-00-00-00')
- @unittest.skipIf(TRAVIS, "EPERM on travis")
+ @unittest.skipIf(TRAVIS, "unreliable on TRAVIS") # raises EPERM
def test_net_if_stats(self):
nics = psutil.net_if_stats()
assert nics, nics
@@ -632,8 +641,7 @@ class TestSystemAPIs(unittest.TestCase):
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
'/proc/diskstats not available on this linux version')
- @unittest.skipIf(APPVEYOR,
- "can't find any physical disk on Appveyor")
+ @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") # no visible disks
def test_disk_io_counters(self):
def check_ntuple(nt):
self.assertEqual(nt[0], nt.read_count)
@@ -687,6 +695,43 @@ class TestSystemAPIs(unittest.TestCase):
if name in ('ctx_switches', 'interrupts'):
self.assertGreater(value, 0)
+ def test_os_constants(self):
+ names = ["POSIX", "WINDOWS", "LINUX", "OSX", "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.OSX
+ names.remove("OSX")
+ 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)
+
if __name__ == '__main__':
run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py
index 962af74c..86910a27 100644..100755
--- a/psutil/tests/test_windows.py
+++ b/psutil/tests/test_windows.py
@@ -18,14 +18,12 @@ import time
import traceback
try:
- import wmi # requires "pip install wmi"
-except ImportError:
- wmi = None
-try:
- import win32api # requires "pip install pypiwin32"
+ import win32api # requires "pip install pypiwin32" / "make setup-dev-env"
import win32con
+ import wmi # requires "pip install wmi" / "make setup-dev-env"
except ImportError:
- win32api = win32con = None
+ if os.name == 'nt':
+ raise
import psutil
from psutil import WINDOWS
@@ -62,7 +60,7 @@ def wrap_exceptions(fun):
return wrapper
-@unittest.skipUnless(WINDOWS, "not a Windows system")
+@unittest.skipUnless(WINDOWS, "WINDOWS only")
class WindowsSpecificTestCase(unittest.TestCase):
@classmethod
@@ -120,13 +118,11 @@ class WindowsSpecificTestCase(unittest.TestCase):
# --- Process class tests
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_name(self):
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
p = psutil.Process(self.pid)
self.assertEqual(p.name(), w.Caption)
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_exe(self):
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
p = psutil.Process(self.pid)
@@ -134,14 +130,12 @@ class WindowsSpecificTestCase(unittest.TestCase):
# Being Windows paths case-insensitive we ignore that.
self.assertEqual(p.exe().lower(), w.ExecutablePath.lower())
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_cmdline(self):
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
p = psutil.Process(self.pid)
self.assertEqual(' '.join(p.cmdline()),
w.CommandLine.replace('"', ''))
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_username(self):
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
p = psutil.Process(self.pid)
@@ -149,7 +143,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
username = "%s\\%s" % (domain, username)
self.assertEqual(p.username(), username)
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_rss_memory(self):
time.sleep(0.1)
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
@@ -157,7 +150,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
rss = p.memory_info().rss
self.assertEqual(rss, int(w.WorkingSetSize))
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_vms_memory(self):
time.sleep(0.1)
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
@@ -171,7 +163,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
if (vms != wmi_usage) and (vms != wmi_usage * 1024):
self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms))
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_process_create_time(self):
w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0]
p = psutil.Process(self.pid)
@@ -188,7 +179,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
num_cpus = int(os.environ['NUMBER_OF_PROCESSORS'])
self.assertEqual(num_cpus, psutil.cpu_count())
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_total_phymem(self):
w = wmi.WMI().Win32_ComputerSystem()[0]
self.assertEqual(int(w.TotalPhysicalMemory),
@@ -207,7 +197,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
#
# Note: this test is not very reliable
- @unittest.skipIf(wmi is None, "wmi module is not installed")
@unittest.skipIf(APPVEYOR, "test not relieable on appveyor")
def test_pids(self):
# Note: this test might fail if the OS is starting/killing
@@ -217,7 +206,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
psutil_pids = set(psutil.pids())
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)
@@ -247,7 +235,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
else:
self.fail("can't find partition %s" % repr(ps_part))
- @unittest.skipIf(win32api is None, "pywin32 module is not installed")
def test_num_handles(self):
p = psutil.Process(os.getpid())
before = p.num_handles()
@@ -258,7 +245,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
win32api.CloseHandle(handle)
self.assertEqual(p.num_handles(), before)
- @unittest.skipIf(win32api is None, "pywin32 module is not installed")
def test_num_handles_2(self):
# Note: this fails from time to time; I'm keen on thinking
# it doesn't mean something is broken
@@ -316,7 +302,6 @@ class WindowsSpecificTestCase(unittest.TestCase):
self.assertRaises(psutil.NoSuchProcess,
p.send_signal, signal.CTRL_BREAK_EVENT)
- @unittest.skipIf(wmi is None, "wmi module is not installed")
def test_net_if_stats(self):
ps_names = set(cext.net_if_stats())
wmi_adapters = wmi.WMI().Win32_NetworkAdapter()
@@ -328,7 +313,7 @@ class WindowsSpecificTestCase(unittest.TestCase):
"no common entries in %s, %s" % (ps_names, wmi_names))
-@unittest.skipUnless(WINDOWS, "not a Windows system")
+@unittest.skipUnless(WINDOWS, "WINDOWS only")
class TestDualProcessImplementation(unittest.TestCase):
"""
Certain APIs on Windows have 2 internal implementations, one
@@ -489,7 +474,7 @@ class TestDualProcessImplementation(unittest.TestCase):
self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID)
-@unittest.skipUnless(WINDOWS, "not a Windows system")
+@unittest.skipUnless(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
@@ -577,7 +562,7 @@ class RemoteProcessTestCase(unittest.TestCase):
self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid()))
-@unittest.skipUnless(WINDOWS, "not a Windows system")
+@unittest.skipUnless(WINDOWS, "WINDOWS only")
class TestServices(unittest.TestCase):
def test_win_service_iter(self):
diff --git a/scripts/disk_usage.py b/scripts/disk_usage.py
index 2bb928c0..37f4da0c 100755
--- a/scripts/disk_usage.py
+++ b/scripts/disk_usage.py
@@ -58,5 +58,6 @@ def main():
part.fstype,
part.mountpoint))
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/scripts/free.py b/scripts/free.py
index 897f006f..82e962ff 100755
--- a/scripts/free.py
+++ b/scripts/free.py
@@ -37,5 +37,6 @@ def main():
'',
''))
+
if __name__ == '__main__':
main()
diff --git a/scripts/ifconfig.py b/scripts/ifconfig.py
index 72cc02fe..b823b374 100755
--- a/scripts/ifconfig.py
+++ b/scripts/ifconfig.py
@@ -8,32 +8,39 @@
A clone of 'ifconfig' on UNIX.
$ python scripts/ifconfig.py
-lo (speed=0MB, duplex=?, mtu=65536, up=yes):
- IPv4 address : 127.0.0.1
- broadcast : 127.0.0.1
- netmask : 255.0.0.0
- IPv6 address : ::1
- netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
- MAC address : 00:00:00:00:00:00
- broadcast : 00:00:00:00:00:00
+lo:
+ stats : speed=0MB, duplex=?, mtu=65536, up=yes
+ incoming : bytes=6889336, pkts=84032, errs=0, drops=0
+ outgoing : bytes=6889336, pkts=84032, errs=0, drops=0
+ IPv4 address : 127.0.0.1
+ netmask : 255.0.0.0
+ IPv6 address : ::1
+ netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+ MAC address : 00:00:00:00:00:00
-wlan0 (speed=0MB, duplex=?, mtu=1500, up=yes):
- IPv4 address : 10.0.3.1
- broadcast : 10.0.3.255
- netmask : 255.255.255.0
- IPv6 address : fe80::3005:adff:fe31:8698
- netmask : ffff:ffff:ffff:ffff::
- MAC address : 32:05:ad:31:86:98
- broadcast : ff:ff:ff:ff:ff:ff
+vboxnet0:
+ stats : speed=10MB, duplex=full, mtu=1500, up=yes
+ incoming : bytes=0, pkts=0, errs=0, drops=0
+ outgoing : bytes=1622766, pkts=9102, errs=0, drops=0
+ IPv4 address : 192.168.33.1
+ broadcast : 192.168.33.255
+ netmask : 255.255.255.0
+ IPv6 address : fe80::800:27ff:fe00:0%vboxnet0
+ netmask : ffff:ffff:ffff:ffff::
+ MAC address : 0a:00:27:00:00:00
+ broadcast : ff:ff:ff:ff:ff:ff
-eth0 (speed=100MB, duplex=full, mtu=1500, up=yes):
- IPv4 address : 192.168.1.2
- broadcast : 192.168.1.255
- netmask : 255.255.255.0
- IPv6 address : fe80::c685:8ff:fe45:641
- netmask : ffff:ffff:ffff:ffff::
- MAC address : c4:85:08:45:06:41
- broadcast : ff:ff:ff:ff:ff:ff
+eth0:
+ stats : speed=0MB, duplex=?, mtu=1500, up=yes
+ incoming : bytes=18905596301, pkts=15178374, errs=0, drops=21
+ outgoing : bytes=1913720087, pkts=9543981, errs=0, drops=0
+ IPv4 address : 10.0.0.3
+ broadcast : 10.255.255.255
+ netmask : 255.0.0.0
+ IPv6 address : fe80::7592:1dcf:bcb7:98d6%wlp3s0
+ netmask : ffff:ffff:ffff:ffff::
+ MAC address : 48:45:20:59:a4:0c
+ broadcast : ff:ff:ff:ff:ff:ff
"""
from __future__ import print_function
@@ -57,22 +64,32 @@ duplex_map = {
def main():
stats = psutil.net_if_stats()
+ io_counters = psutil.net_io_counters(pernic=True)
for nic, addrs in psutil.net_if_addrs().items():
+ print("%s:" % (nic))
if nic in stats:
- print("%s (speed=%sMB, duplex=%s, mtu=%s, up=%s):" % (
- nic, stats[nic].speed, duplex_map[stats[nic].duplex],
- stats[nic].mtu, "yes" if stats[nic].isup else "no"))
- else:
- print("%s:" % (nic))
+ st = stats[nic]
+ print(" stats : ", end='')
+ print("speed=%sMB, duplex=%s, mtu=%s, up=%s" % (
+ st.speed, duplex_map[st.duplex], st.mtu,
+ "yes" if st.isup else "no"))
+ if nic in io_counters:
+ io = io_counters[nic]
+ print(" incoming : ", end='')
+ print("bytes=%s, pkts=%s, errs=%s, drops=%s" % (
+ io.bytes_recv, io.packets_recv, io.errin, io.dropin))
+ print(" outgoing : ", end='')
+ print("bytes=%s, pkts=%s, errs=%s, drops=%s" % (
+ io.bytes_sent, io.packets_sent, io.errout, io.dropout))
for addr in addrs:
- print(" %-8s" % af_map.get(addr.family, addr.family), end="")
+ print(" %-4s" % af_map.get(addr.family, addr.family), end="")
print(" address : %s" % addr.address)
if addr.broadcast:
- print(" broadcast : %s" % addr.broadcast)
+ print(" broadcast : %s" % addr.broadcast)
if addr.netmask:
- print(" netmask : %s" % addr.netmask)
+ print(" netmask : %s" % addr.netmask)
if addr.ptp:
- print(" p2p : %s" % addr.ptp)
+ print(" p2p : %s" % addr.ptp)
print("")
diff --git a/scripts/internal/bench_oneshot.py b/scripts/internal/bench_oneshot.py
index f2609d2b..2ef21729 100755
--- a/scripts/internal/bench_oneshot.py
+++ b/scripts/internal/bench_oneshot.py
@@ -18,15 +18,26 @@ import textwrap
import psutil
-ITERATIONS = 10000
+ITERATIONS = 1000
# The list of Process methods which gets collected in one shot and
# as such get advantage of the speedup.
+names = [
+ 'cpu_times',
+ 'cpu_percent',
+ 'memory_info',
+ 'memory_percent',
+ 'ppid',
+ 'parent',
+]
+
+if psutil.POSIX:
+ names.append('uids')
+ names.append('username')
+
if psutil.LINUX:
- names = (
- 'cpu_percent',
+ names += [
'cpu_times',
- 'create_time',
'gids',
'name',
'num_ctx_switches',
@@ -35,53 +46,61 @@ if psutil.LINUX:
'status',
'terminal',
'uids',
- )
-elif psutil.WINDOWS:
- names = (
- 'cpu_affinity',
- 'cpu_percent',
- 'cpu_times',
- 'io_counters',
- 'ionice',
- 'memory_info',
- 'memory_percent',
- 'nice',
- 'num_handles',
- )
+ ]
elif psutil.BSD:
- names = (
- 'cpu_percent',
+ names = [
'cpu_times',
- 'create_time',
'gids',
'io_counters',
'memory_full_info',
'memory_info',
- 'memory_percent',
+ 'name',
'num_ctx_switches',
'ppid',
'status',
'terminal',
'uids',
- )
+ ]
elif psutil.SUNOS:
- names = (
+ names += [
'cmdline',
- 'create_time',
'gids',
'memory_full_info',
'memory_info',
- 'memory_percent',
'name',
'num_threads',
'ppid',
'status',
'terminal',
'uids',
+ ]
+elif psutil.OSX:
+ names += [
+ 'cpu_times',
+ 'create_time',
+ 'gids',
+ 'memory_info',
+ 'name',
+ 'num_ctx_switches',
+ 'num_threads',
+ 'ppid',
+ 'terminal',
+ 'uids',
+ ]
+elif psutil.WINDOWS:
+ names = (
+ 'cpu_affinity',
+ 'cpu_percent',
+ 'cpu_times',
+ 'io_counters',
+ 'ionice',
+ 'memory_info',
+ 'memory_percent',
+ 'nice',
+ 'num_handles',
)
-else:
- raise RuntimeError("platform %r not supported" % sys.platform)
+names = sorted(set(names))
setup = textwrap.dedent("""
from __main__ import names
@@ -126,4 +145,5 @@ def main():
print("same speed")
-main()
+if __name__ == '__main__':
+ main()
diff --git a/scripts/internal/bench_oneshot_2.py b/scripts/internal/bench_oneshot_2.py
new file mode 100644
index 00000000..b5758149
--- /dev/null
+++ b/scripts/internal/bench_oneshot_2.py
@@ -0,0 +1,56 @@
+#!/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.
+
+"""
+Same as bench_oneshot.py but uses perf module instead, which is
+supposed to be more precise.
+"""
+
+import sys
+
+import perf.text_runner
+
+import psutil
+from bench_oneshot import names
+
+
+p = psutil.Process()
+funs = [getattr(p, n) for n in names]
+
+
+def call_normal(funs):
+ for fun in funs:
+ fun()
+
+
+def call_oneshot(funs):
+ with p.oneshot():
+ for fun in funs:
+ fun()
+
+
+def prepare_cmd(runner, cmd):
+ cmd.append(runner.args.benchmark)
+
+
+def main():
+ runner = perf.text_runner.TextRunner(name='psutil')
+ runner.argparser.add_argument('benchmark', choices=('normal', 'oneshot'))
+ runner.prepare_subprocess_args = prepare_cmd
+
+ args = runner.parse_args()
+ if not args.worker:
+ print("%s methods involved on platform %r:" % (
+ len(names), sys.platform))
+ for name in sorted(names):
+ print(" " + name)
+
+ if args.benchmark == 'normal':
+ runner.bench_func(call_normal, funs)
+ else:
+ runner.bench_func(call_oneshot, funs)
+
+main()
diff --git a/.ci/appveyor/download_exes.py b/scripts/internal/download_exes.py
index 37ebdfd1..2a40168c 100755
--- a/.ci/appveyor/download_exes.py
+++ b/scripts/internal/download_exes.py
@@ -23,11 +23,18 @@ import sys
from concurrent.futures import ThreadPoolExecutor
+from psutil import __version__ as PSUTIL_VERSION
+
BASE_URL = 'https://ci.appveyor.com/api'
PY_VERSIONS = ['2.7', '3.3', '3.4', '3.5']
+def exit(msg):
+ print(hilite(msg, ok=False), file=sys.stderr)
+ sys.exit(1)
+
+
def term_supports_colors(file=sys.stdout):
try:
import curses
@@ -106,19 +113,19 @@ def get_file_urls(options):
file_url = job_url + '/' + item['fileName']
urls.append(file_url)
if not urls:
- sys.exit("no artifacts found")
+ exit("no artifacts found")
for url in sorted(urls, key=lambda x: os.path.basename(x)):
yield url
def rename_27_wheels():
# See: https://github.com/giampaolo/psutil/issues/810
- src = 'dist/psutil-4.3.0-cp27-cp27m-win32.whl'
- dst = 'dist/psutil-4.3.0-cp27-none-win32.whl'
+ src = 'dist/psutil-%s-cp27-cp27m-win32.whl' % PSUTIL_VERSION
+ dst = 'dist/psutil-%s-cp27-none-win32.whl' % PSUTIL_VERSION
print("rename: %s\n %s" % (src, dst))
os.rename(src, dst)
- src = 'dist/psutil-4.3.0-cp27-cp27m-win_amd64.whl'
- dst = 'dist/psutil-4.3.0-cp27-none-win_amd64.whl'
+ src = 'dist/psutil-%s-cp27-cp27m-win_amd64.whl' % PSUTIL_VERSION
+ dst = 'dist/psutil-%s-cp27-none-win_amd64.whl' % PSUTIL_VERSION
print("rename: %s\n %s" % (src, dst))
os.rename(src, dst)
@@ -134,8 +141,7 @@ def main(options):
expected = len(PY_VERSIONS) * 4
got = len(files)
if expected != got:
- print(hilite("expected %s files, got %s" % (expected, got), ok=False),
- file=sys.stderr)
+ return exit("expected %s files, got %s" % (expected, got))
rename_27_wheels()
diff --git a/scripts/internal/print_announce.py b/scripts/internal/print_announce.py
new file mode 100755
index 00000000..e47911c2
--- /dev/null
+++ b/scripts/internal/print_announce.py
@@ -0,0 +1,116 @@
+#!/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.
+
+"""
+Prints release announce based on HISTORY.rst file content.
+"""
+
+import os
+import re
+
+from psutil import __version__ as PRJ_VERSION
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+HISTORY = os.path.abspath(os.path.join(HERE, '../../HISTORY.rst'))
+
+PRJ_NAME = 'psutil'
+PRJ_URL_HOME = 'https://github.com/giampaolo/psutil'
+PRJ_URL_DOC = 'http://pythonhosted.org/psutil'
+PRJ_URL_DOWNLOAD = 'https://pypi.python.org/pypi/psutil'
+PRJ_URL_WHATSNEW = \
+ 'https://github.com/giampaolo/psutil/blob/master/HISTORY.rst'
+
+template = """\
+Hello all,
+I'm glad to announce the release of {prj_name} {prj_version}:
+{prj_urlhome}
+
+About
+=====
+
+psutil (process and system utilities) is a cross-platform library for \
+retrieving information on running processes and system utilization (CPU, \
+memory, disks, network) in Python. It is useful mainly for system \
+monitoring, profiling and limiting process resources and management of \
+running processes. It implements many functionalities offered by command \
+line tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, \
+nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It \
+currently supports Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD and \
+NetBSD, both 32-bit and 64-bit architectures, with Python versions from 2.6 \
+to 3.5 (users of Python 2.4 and 2.5 may use 2.1.3 version). PyPy is also \
+known to work.
+
+What's new
+==========
+
+{changes}
+
+Links
+=====
+
+- Home page: {prj_urlhome}
+- Download: {prj_urldownload}
+- Documentation: {prj_urldoc}
+- What's new: {prj_urlwhatsnew}
+
+--
+
+Giampaolo - http://grodola.blogspot.com
+"""
+
+
+def get_changes():
+ """Get the most recent changes for this release by parsing
+ HISTORY.rst file.
+ """
+ with open(HISTORY) as f:
+ lines = f.readlines()
+
+ block = []
+
+ # eliminate the part preceding the first block
+ for i, line in enumerate(lines):
+ line = lines.pop(0)
+ if line.startswith('===='):
+ break
+ lines.pop(0)
+
+ for i, line in enumerate(lines):
+ line = lines.pop(0)
+ line = line.rstrip()
+ if re.match("^- \d+_: ", line):
+ num, _, rest = line.partition(': ')
+ num = ''.join([x for x in num if x.isdigit()])
+ line = "- #%s: %s" % (num, rest)
+
+ if line.startswith('===='):
+ break
+ block.append(line)
+
+ # eliminate bottom empty lines
+ block.pop(-1)
+ while not block[-1]:
+ block.pop(-1)
+
+ return "\n".join(block)
+
+
+def main():
+ changes = get_changes()
+ print(template.format(
+ prj_name=PRJ_NAME,
+ prj_version=PRJ_VERSION,
+ prj_urlhome=PRJ_URL_HOME,
+ prj_urldownload=PRJ_URL_DOWNLOAD,
+ prj_urldoc=PRJ_URL_DOC,
+ prj_urlwhatsnew=PRJ_URL_WHATSNEW,
+ changes=changes,
+ ))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py
new file mode 100755
index 00000000..9663b378
--- /dev/null
+++ b/scripts/internal/winmake.py
@@ -0,0 +1,322 @@
+#!/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.
+
+
+"""Shortcuts for various tasks, emulating UNIX "make" on Windows.
+This is supposed to be invoked by "make.bat" and not used directly.
+This was originally written as a bat file but they suck so much
+that they should be deemed illegal!
+"""
+
+import errno
+import fnmatch
+import functools
+import os
+import shutil
+import ssl
+import subprocess
+import sys
+import tempfile
+import textwrap
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+ROOT = os.path.abspath(os.path.join(HERE, '../..'))
+PYTHON = sys.executable
+TSCRIPT = os.environ['TSCRIPT']
+GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
+PY3 = sys.version_info[0] == 3
+DEPS = [
+ "coverage",
+ "flake8",
+ "ipaddress",
+ "mock",
+ "nose",
+ "pdbpp",
+ "perf",
+ "pip",
+ "pypiwin32",
+ "setuptools",
+ "unittest2",
+ "wheel",
+ "wmi",
+]
+_cmds = {}
+
+
+# ===================================================================
+# utils
+# ===================================================================
+
+
+def sh(cmd):
+ print("cmd: " + cmd)
+ code = os.system(cmd)
+ if code:
+ raise SystemExit
+
+
+def cmd(fun):
+ @functools.wraps(fun)
+ def wrapper(*args, **kwds):
+ return fun(*args, **kwds)
+
+ _cmds[fun.__name__] = fun.__doc__
+ return wrapper
+
+
+def rm(pattern, directory=False):
+ """Recursively remove a file or dir by pattern."""
+ def safe_remove(path):
+ try:
+ os.remove(path)
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ raise
+ else:
+ print("rm %s" % path)
+
+ def safe_rmtree(path):
+ def onerror(fun, path, excinfo):
+ exc = excinfo[1]
+ if exc.errno != errno.ENOENT:
+ raise
+
+ existed = os.path.isdir(path)
+ shutil.rmtree(path, onerror=onerror)
+ if existed:
+ print("rmdir -f %s" % path)
+
+ if "*" not in pattern:
+ if directory:
+ safe_rmtree(pattern)
+ else:
+ safe_remove(pattern)
+ return
+
+ for root, subdirs, subfiles in os.walk('.'):
+ root = os.path.normpath(root)
+ if root.startswith('.git/'):
+ continue
+ found = fnmatch.filter(subdirs if directory else subfiles, pattern)
+ for name in found:
+ path = os.path.join(root, name)
+ if directory:
+ print("rmdir -f %s" % path)
+ safe_rmtree(path)
+ else:
+ print("rm %s" % path)
+ safe_remove(path)
+
+
+def install_pip():
+ try:
+ import pip # NOQA
+ except ImportError:
+ if PY3:
+ from urllib.request import urlopen
+ else:
+ from urllib2 import urlopen
+
+ if hasattr(ssl, '_create_unverified_context'):
+ ctx = ssl._create_unverified_context()
+ else:
+ ctx = None
+ kw = dict(context=ctx) if ctx else {}
+ print("downloading %s" % GET_PIP_URL)
+ req = urlopen(GET_PIP_URL, **kw)
+ data = req.read()
+
+ tfile = os.path.join(tempfile.gettempdir(), 'get-pip.py')
+ with open(tfile, 'wb') as f:
+ f.write(data)
+
+ try:
+ sh('%s %s --user' % (PYTHON, tfile))
+ finally:
+ os.remove(tfile)
+
+
+# ===================================================================
+# commands
+# ===================================================================
+
+
+@cmd
+def help():
+ """Print this help"""
+ print('Run "make <target>" where <target> is one of:')
+ for name in sorted(_cmds):
+ print(" %-20s %s" % (name.replace('_', '-'), _cmds[name] or ''))
+
+
+@cmd
+def build():
+ """Build / compile"""
+ sh("%s setup.py build" % PYTHON)
+ # copies compiled *.pyd files in ./psutil directory in order to
+ # allow "import psutil" when using the interactive interpreter
+ # from within this directory.
+ sh("%s setup.py build_ext -i" % PYTHON)
+
+
+@cmd
+def install():
+ """Install in develop / edit mode"""
+ install_git_hooks()
+ build()
+ sh("%s setup.py develop" % PYTHON)
+
+
+@cmd
+def uninstall():
+ """Uninstall psutil"""
+ install_pip()
+ sh("%s -m pip uninstall -y psutil" % PYTHON)
+
+
+@cmd
+def clean():
+ """Deletes dev files"""
+ rm("*.egg-info", directory=True)
+ rm("*__pycache__", directory=True)
+ rm("build", directory=True)
+ rm("dist", directory=True)
+ rm("htmlcov", directory=True)
+ rm("tmp", directory=True)
+
+ rm("*.bak")
+ rm("*.core")
+ rm("*.orig")
+ rm("*.pyc")
+ rm("*.pyo")
+ rm("*.rej")
+ rm("*.so")
+ rm("*.~")
+ rm(".coverage")
+ rm(".tox")
+
+
+@cmd
+def setup_dev_env():
+ """Install useful deps"""
+ install_pip()
+ install_git_hooks()
+ sh("%s -m pip install -U %s" % (PYTHON, " ".join(DEPS)))
+
+
+@cmd
+def flake8():
+ """Run flake8 against all py files"""
+ py_files = subprocess.check_output("git ls-files")
+ if PY3:
+ py_files = py_files.decode()
+ py_files = [x for x in py_files.split() if x.endswith('.py')]
+ py_files = ' '.join(py_files)
+ sh("%s -m flake8 %s" % (PYTHON, py_files))
+
+
+@cmd
+def test():
+ """Run tests"""
+ install()
+ sh("%s %s" % (PYTHON, TSCRIPT))
+
+
+@cmd
+def coverage():
+ """Run coverage tests."""
+ # Note: coverage options are controlled by .coveragerc file
+ install()
+ sh("%s -m coverage run %s" % (PYTHON, TSCRIPT))
+ sh("%s -m coverage report" % PYTHON)
+ sh("%s -m coverage html" % PYTHON)
+ sh("%s -m webbrowser -t htmlcov/index.html" % PYTHON)
+
+
+@cmd
+def test_process():
+ """Run process tests"""
+ install()
+ sh("%s -m unittest -v psutil.tests.test_process" % PYTHON)
+
+
+@cmd
+def test_system():
+ """Run system tests"""
+ install()
+ sh("%s -m unittest -v psutil.tests.test_system" % PYTHON)
+
+
+@cmd
+def test_platform():
+ """Run windows only tests"""
+ install()
+ sh("%s -m unittest -v psutil.tests.test_windows" % PYTHON)
+
+
+@cmd
+def test_misc():
+ """Run misc tests"""
+ install()
+ sh("%s -m unittest -v psutil.tests.test_misc" % PYTHON)
+
+
+@cmd
+def test_by_name():
+ """Run test by name"""
+ try:
+ print(sys.argv)
+ name = sys.argv[2]
+ except IndexError:
+ sys.exit('second arg missing')
+ install()
+ sh(textwrap.dedent("""\
+ %s -m nose \
+ psutil\\tests\\test_process.py \
+ psutil\\tests\\test_system.py \
+ psutil\\tests\\test_windows.py \
+ psutil\\tests\\test_misc.py --nocapture -v -m %s""" % (PYTHON, name)))
+
+
+@cmd
+def test_memleaks():
+ """Run memory leaks tests"""
+ install()
+ sh("%s test\test_memory_leaks.py" % PYTHON)
+
+
+@cmd
+def install_git_hooks():
+ shutil.copy(".git-pre-commit", ".git/hooks/pre-commit")
+
+
+@cmd
+def bench_oneshot():
+ sh("%s scripts\\internal\\bench_oneshot.py" % PYTHON)
+
+
+@cmd
+def bench_oneshot_2():
+ sh("%s scripts\\internal\\bench_oneshot_2.py" % PYTHON)
+
+
+def main():
+ os.chdir(ROOT)
+ try:
+ cmd = sys.argv[1].replace('-', '_')
+ except IndexError:
+ return help()
+
+ if cmd in _cmds:
+ fun = getattr(sys.modules[__name__], cmd)
+ fun()
+ else:
+ help()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/iotop.py b/scripts/iotop.py
index 1a8d8ba5..9f76eb1c 100755
--- a/scripts/iotop.py
+++ b/scripts/iotop.py
@@ -48,6 +48,7 @@ def tear_down():
curses.echo()
curses.endwin()
+
win = curses.initscr()
atexit.register(tear_down)
curses.endwin()
@@ -176,5 +177,6 @@ def main():
except (KeyboardInterrupt, SystemExit):
pass
+
if __name__ == '__main__':
main()
diff --git a/scripts/killall.py b/scripts/killall.py
index b548e7bc..f9cc9201 100755
--- a/scripts/killall.py
+++ b/scripts/killall.py
@@ -29,4 +29,6 @@ def main():
else:
sys.exit(0)
-sys.exit(main())
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/meminfo.py b/scripts/meminfo.py
index 3546960b..88c3a937 100755
--- a/scripts/meminfo.py
+++ b/scripts/meminfo.py
@@ -64,5 +64,6 @@ def main():
print('\nSWAP\n----')
pprint_ntuple(psutil.swap_memory())
+
if __name__ == '__main__':
main()
diff --git a/scripts/netstat.py b/scripts/netstat.py
index a5e171bc..1426cd76 100755
--- a/scripts/netstat.py
+++ b/scripts/netstat.py
@@ -60,5 +60,6 @@ def main():
proc_names.get(c.pid, '?')[:15],
))
+
if __name__ == '__main__':
main()
diff --git a/scripts/nettop.py b/scripts/nettop.py
index acfa6500..97f80aad 100755
--- a/scripts/nettop.py
+++ b/scripts/nettop.py
@@ -161,5 +161,6 @@ def main():
except (KeyboardInterrupt, SystemExit):
pass
+
if __name__ == '__main__':
main()
diff --git a/scripts/pidof.py b/scripts/pidof.py
index 0db9563e..1c23900f 100755
--- a/scripts/pidof.py
+++ b/scripts/pidof.py
@@ -50,5 +50,6 @@ def main():
if pids:
print(" ".join(pids))
+
if __name__ == '__main__':
main()
diff --git a/scripts/pmap.py b/scripts/pmap.py
index f0d53355..16eebb60 100755
--- a/scripts/pmap.py
+++ b/scripts/pmap.py
@@ -53,5 +53,6 @@ def main():
print("-" * 33)
print(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
+
if __name__ == '__main__':
main()
diff --git a/scripts/procinfo.py b/scripts/procinfo.py
index 9990086f..8dc34c45 100755
--- a/scripts/procinfo.py
+++ b/scripts/procinfo.py
@@ -8,37 +8,111 @@
Print detailed information about a process.
Author: Giampaolo Rodola' <g.rodola@gmail.com>
-$ python scripts/process_detail.py
-pid 820
-name python
-exe /usr/bin/python2.7
-parent 29613 (bash)
-cmdline python scripts/process_detail.py
-started 2014-41-27 03:41
-user giampaolo
-uids real=1000, effective=1000, saved=1000
-gids real=1000, effective=1000, saved=1000
-terminal /dev/pts/17
-cwd /ssd/svn/psutil
-memory 0.1% (resident=10.6M, virtual=58.5M)
-cpu 0.0% (user=0.09, system=0.0)
-status running
-niceness 0
-num threads 1
-I/O bytes-read=0B, bytes-written=0B
-open files
-running threads id=820, user-time=0.09, sys-time=0.0
+$ python scripts/procinfo.py
+pid 4600
+name chrome
+parent 4554 (bash)
+exe /opt/google/chrome/chrome
+cwd /home/giampaolo
+cmdline /opt/google/chrome/chrome
+started 2016-09-19 11:12
+cpu-tspent 27:27.68
+cpu-times user=8914.32, system=3530.59,
+ children_user=1.46, children_system=1.31
+cpu-affinity [0, 1, 2, 3, 4, 5, 6, 7]
+memory rss=520.5M, vms=1.9G, shared=132.6M, text=95.0M, lib=0B,
+ data=816.5M, dirty=0B
+memory % 3.26
+user giampaolo
+uids real=1000, effective=1000, saved=1000
+uids real=1000, effective=1000, saved=1000
+terminal /dev/pts/2
+status sleeping
+nice 0
+ionice class=IOPriority.IOPRIO_CLASS_NONE, value=0
+num-threads 47
+num-fds 379
+I/O read_count=96.6M, write_count=80.7M,
+ read_bytes=293.2M, write_bytes=24.5G
+ctx-switches voluntary=30426463, involuntary=460108
+children PID NAME
+ 4605 cat
+ 4606 cat
+ 4609 chrome
+ 4669 chrome
+open-files PATH
+ /opt/google/chrome/icudtl.dat
+ /opt/google/chrome/snapshot_blob.bin
+ /opt/google/chrome/natives_blob.bin
+ /opt/google/chrome/chrome_100_percent.pak
+ [...]
+connections PROTO LOCAL ADDR REMOTE ADDR STATUS
+ UDP 10.0.0.3:3693 *:* NONE
+ TCP 10.0.0.3:55102 172.217.22.14:443 ESTABLISHED
+ UDP 10.0.0.3:35172 *:* NONE
+ TCP 10.0.0.3:32922 172.217.16.163:443 ESTABLISHED
+ UDP :::5353 *:* NONE
+ UDP 10.0.0.3:59925 *:* NONE
+threads TID USER SYSTEM
+ 11795 0.7 1.35
+ 11796 0.68 1.37
+ 15887 0.74 0.03
+ 19055 0.77 0.01
+ [...]
+ total=47
+res-limits RLIMIT SOFT HARD
+ virtualmem infinity infinity
+ coredumpsize 0 infinity
+ cputime infinity infinity
+ datasize infinity infinity
+ filesize infinity infinity
+ locks infinity infinity
+ memlock 65536 65536
+ msgqueue 819200 819200
+ nice 0 0
+ openfiles 8192 65536
+ maxprocesses 63304 63304
+ rss infinity infinity
+ realtimeprio 0 0
+ rtimesched infinity infinity
+ sigspending 63304 63304
+ stack 8388608 infinity
+mem-maps RSS PATH
+ 381.4M [anon]
+ 62.8M /opt/google/chrome/chrome
+ 15.8M /home/giampaolo/.config/google-chrome/Default/History
+ 6.6M /home/giampaolo/.config/google-chrome/Default/Favicons
+ [...]
"""
+import argparse
import datetime
-import os
import socket
import sys
import psutil
-POSIX = os.name == 'posix'
+ACCESS_DENIED = ''
+NON_VERBOSE_ITERATIONS = 4
+RLIMITS_MAP = {
+ "RLIMIT_AS": "virtualmem",
+ "RLIMIT_CORE": "coredumpsize",
+ "RLIMIT_CPU": "cputime",
+ "RLIMIT_DATA": "datasize",
+ "RLIMIT_FSIZE": "filesize",
+ "RLIMIT_LOCKS": "locks",
+ "RLIMIT_MEMLOCK": "memlock",
+ "RLIMIT_MSGQUEUE": "msgqueue",
+ "RLIMIT_NICE": "nice",
+ "RLIMIT_NOFILE": "openfiles",
+ "RLIMIT_NPROC": "maxprocesses",
+ "RLIMIT_RSS": "rss",
+ "RLIMIT_RTPRIO": "realtimeprio",
+ "RLIMIT_RTTIME": "rtimesched",
+ "RLIMIT_SIGPENDING": "sigspending",
+ "RLIMIT_STACK": "stack",
+}
def convert_bytes(n):
@@ -54,87 +128,127 @@ def convert_bytes(n):
def print_(a, b):
- if sys.stdout.isatty() and POSIX:
- fmt = '\x1b[1;32m%-17s\x1b[0m %s' % (a, b)
+ if sys.stdout.isatty() and psutil.POSIX:
+ fmt = '\x1b[1;32m%-13s\x1b[0m %s' % (a, b)
else:
- fmt = '%-15s %s' % (a, b)
+ fmt = '%-11s %s' % (a, b)
print(fmt)
-def run(pid):
- ACCESS_DENIED = ''
+def str_ntuple(nt, bytes2human=False):
+ if nt == ACCESS_DENIED:
+ return ""
+ if not bytes2human:
+ return ", ".join(["%s=%s" % (x, getattr(nt, x)) for x in nt._fields])
+ else:
+ return ", ".join(["%s=%s" % (x, convert_bytes(getattr(nt, x)))
+ for x in nt._fields])
+
+
+def run(pid, verbose=False):
try:
- p = psutil.Process(pid)
- pinfo = p.as_dict(ad_value=ACCESS_DENIED)
+ proc = psutil.Process(pid)
+ pinfo = proc.as_dict(ad_value=ACCESS_DENIED)
except psutil.NoSuchProcess as err:
sys.exit(str(err))
- try:
- parent = p.parent()
- if parent:
- parent = '(%s)' % parent.name()
- else:
+ # collect other proc info
+ with proc.oneshot():
+ try:
+ parent = proc.parent()
+ if parent:
+ parent = '(%s)' % parent.name()
+ else:
+ parent = ''
+ except psutil.Error:
parent = ''
- except psutil.Error:
- parent = ''
- if pinfo['create_time'] != ACCESS_DENIED:
- started = datetime.datetime.fromtimestamp(
- pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
- else:
- started = ACCESS_DENIED
- io = pinfo.get('io_counters', ACCESS_DENIED)
- if pinfo['memory_info'] != ACCESS_DENIED:
- mem = '%s%% (resident=%s, virtual=%s) ' % (
- round(pinfo['memory_percent'], 1),
- convert_bytes(pinfo['memory_info'].rss),
- convert_bytes(pinfo['memory_info'].vms))
- else:
- mem = ACCESS_DENIED
- children = p.children()
+ try:
+ pinfo['children'] = proc.children()
+ except psutil.Error:
+ pinfo['children'] = []
+ if pinfo['create_time']:
+ started = datetime.datetime.fromtimestamp(
+ pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
+ else:
+ started = ACCESS_DENIED
+ # here we go
print_('pid', pinfo['pid'])
print_('name', pinfo['name'])
- print_('exe', pinfo['exe'])
print_('parent', '%s %s' % (pinfo['ppid'], parent))
+ print_('exe', pinfo['exe'])
+ print_('cwd', pinfo['cwd'])
print_('cmdline', ' '.join(pinfo['cmdline']))
print_('started', started)
+
+ cpu_tot_time = datetime.timedelta(seconds=sum(pinfo['cpu_times']))
+ cpu_tot_time = "%s:%s.%s" % (
+ cpu_tot_time.seconds // 60 % 60,
+ str((cpu_tot_time.seconds % 60)).zfill(2),
+ str(cpu_tot_time.microseconds)[:2])
+ print_('cpu-tspent', cpu_tot_time)
+ print_('cpu-times', str_ntuple(pinfo['cpu_times']))
+ if hasattr(proc, "cpu_affinity"):
+ print_("cpu-affinity", pinfo["cpu_affinity"])
+
+ print_('memory', str_ntuple(pinfo['memory_info'], bytes2human=True))
+ print_('memory %', round(pinfo['memory_percent'], 2))
print_('user', pinfo['username'])
- if POSIX and pinfo['uids'] and pinfo['gids']:
- print_('uids', 'real=%s, effective=%s, saved=%s' % pinfo['uids'])
- if POSIX and pinfo['gids']:
- print_('gids', 'real=%s, effective=%s, saved=%s' % pinfo['gids'])
- if POSIX:
+ if psutil.POSIX:
+ print_('uids', str_ntuple(pinfo['uids']))
+ if psutil.POSIX:
+ print_('uids', str_ntuple(pinfo['uids']))
+ if psutil.POSIX:
print_('terminal', pinfo['terminal'] or '')
- print_('cwd', pinfo['cwd'])
- print_('memory', mem)
- print_('cpu', '%s%% (user=%s, system=%s)' % (
- pinfo['cpu_percent'],
- getattr(pinfo['cpu_times'], 'user', '?'),
- getattr(pinfo['cpu_times'], 'system', '?')))
+
print_('status', pinfo['status'])
- print_('niceness', pinfo['nice'])
- print_('num threads', pinfo['num_threads'])
- if io != ACCESS_DENIED:
- print_('I/O', 'bytes-read=%s, bytes-written=%s' % (
- convert_bytes(io.read_bytes),
- convert_bytes(io.write_bytes)))
- if children:
- print_('children', '')
- for child in children:
- print_('', 'pid=%s name=%s' % (child.pid, child.name()))
-
- if pinfo['open_files'] != ACCESS_DENIED and pinfo['open_files']:
- print_('open files', '')
- for file in pinfo['open_files']:
- print_('', 'fd=%s %s ' % (file.fd, file.path))
+ print_('nice', pinfo['nice'])
+ if hasattr(proc, "ionice"):
+ try:
+ ionice = proc.ionice()
+ except psutil.Error:
+ pass
+ else:
+ if psutil.WINDOWS:
+ print_("ionice", ionice)
+ else:
+ print_("ionice", "class=%s, value=%s" % (
+ str(ionice.ioclass), ionice.value))
- if pinfo['threads'] and len(pinfo['threads']) > 1:
- print_('running threads', '')
- for thread in pinfo['threads']:
- print_('', 'id=%s, user-time=%s, sys-time=%s' % (
- thread.id, thread.user_time, thread.system_time))
- if pinfo['connections'] not in (ACCESS_DENIED, []):
- print_('open connections', '')
+ print_('num-threads', pinfo['num_threads'])
+ if psutil.POSIX:
+ print_('num-fds', pinfo['num_fds'])
+ if psutil.WINDOWS:
+ print_('num-handles', pinfo['num_handles'])
+
+ if 'io_counters' in pinfo:
+ print_('I/O', str_ntuple(pinfo['io_counters'], bytes2human=True))
+ print_("ctx-switches", str_ntuple(pinfo['num_ctx_switches']))
+ if pinfo['children']:
+ template = "%-6s %s"
+ print_("children", template % ("PID", "NAME"))
+ for child in pinfo['children']:
+ try:
+ print_('', template % (child.pid, child.name()))
+ except psutil.AccessDenied:
+ print_('', template % (child.pid, ""))
+ except psutil.NoSuchProcess:
+ pass
+
+ if pinfo['open_files']:
+ print_('open-files', 'PATH')
+ for i, file in enumerate(pinfo['open_files']):
+ if not verbose and i >= NON_VERBOSE_ITERATIONS:
+ print_("", "[...]")
+ break
+ print_('', file.path)
+ else:
+ print_('open-files', '')
+
+ if pinfo['connections']:
+ template = '%-5s %-25s %-25s %s'
+ print_('connections',
+ template % ('PROTO', 'LOCAL ADDR', 'REMOTE ADDR', 'STATUS'))
for conn in pinfo['connections']:
if conn.type == socket.SOCK_STREAM:
type = 'TCP'
@@ -147,19 +261,76 @@ def run(pid):
rip, rport = '*', '*'
else:
rip, rport = conn.raddr
- print_('', '%s:%s -> %s:%s type=%s status=%s' % (
- lip, lport, rip, rport, type, conn.status))
+ print_('', template % (
+ type,
+ "%s:%s" % (lip, lport),
+ "%s:%s" % (rip, rport),
+ conn.status))
+ else:
+ print_('connections', '')
+
+ if pinfo['threads'] and len(pinfo['threads']) > 1:
+ template = "%-5s %12s %12s"
+ print_('threads', template % ("TID", "USER", "SYSTEM"))
+ for i, thread in enumerate(pinfo['threads']):
+ if not verbose and i >= NON_VERBOSE_ITERATIONS:
+ print_("", "[...]")
+ break
+ print_('', template % thread)
+ print_('', "total=%s" % len(pinfo['threads']))
+ else:
+ print_('threads', '')
+
+ if hasattr(proc, "rlimit"):
+ res_names = [x for x in dir(psutil) if x.startswith("RLIMIT")]
+ resources = []
+ for res_name in res_names:
+ try:
+ soft, hard = proc.rlimit(getattr(psutil, res_name))
+ except psutil.AccessDenied:
+ pass
+ else:
+ resources.append((res_name, soft, hard))
+ if resources:
+ template = "%-12s %15s %15s"
+ print_("res-limits", template % ("RLIMIT", "SOFT", "HARD"))
+ for res_name, soft, hard in resources:
+ if soft == psutil.RLIM_INFINITY:
+ soft = "infinity"
+ if hard == psutil.RLIM_INFINITY:
+ hard = "infinity"
+ print_('', template % (
+ RLIMITS_MAP.get(res_name, res_name), soft, hard))
+
+ if hasattr(proc, "environ") and pinfo['environ']:
+ template = "%-25s %s"
+ print_("environ", template % ("NAME", "VALUE"))
+ for i, k in enumerate(sorted(pinfo['environ'])):
+ if not verbose and i >= NON_VERBOSE_ITERATIONS:
+ print_("", "[...]")
+ break
+ print_("", template % (k, pinfo['environ'][k]))
+
+ if pinfo.get('memory_maps', None):
+ template = "%-8s %s"
+ print_("mem-maps", template % ("RSS", "PATH"))
+ maps = sorted(pinfo['memory_maps'], key=lambda x: x.rss, reverse=True)
+ for i, region in enumerate(maps):
+ if not verbose and i >= NON_VERBOSE_ITERATIONS:
+ print_("", "[...]")
+ break
+ print_("", template % (convert_bytes(region.rss), region.path))
def main(argv=None):
- if argv is None:
- argv = sys.argv
- if len(argv) == 1:
- sys.exit(run(os.getpid()))
- elif len(argv) == 2:
- sys.exit(run(int(argv[1])))
- else:
- sys.exit('usage: %s [pid]' % __file__)
+ parser = argparse.ArgumentParser(
+ description="print information about a process")
+ parser.add_argument("pid", type=int, help="process pid")
+ parser.add_argument('--verbose', '-v', action='store_true',
+ help="print more info")
+ args = parser.parse_args()
+ run(args.pid, args.verbose)
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/scripts/procsmem.py b/scripts/procsmem.py
index 7c14dca3..d7b53f62 100755
--- a/scripts/procsmem.py
+++ b/scripts/procsmem.py
@@ -79,24 +79,25 @@ def main():
p._info = info
procs.append(p)
- procs.sort(key=lambda p: p._uss)
- templ = "%-7s %-7s %-30s %7s %7s %7s %7s"
- print(templ % ("PID", "User", "Cmdline", "USS", "PSS", "Swap", "RSS"))
- print("=" * 78)
- for p in procs:
- line = templ % (
- p.pid,
- p._info["username"][:7],
- " ".join(p._info["cmdline"])[:30],
- convert_bytes(p._uss),
- convert_bytes(p._pss) if p._pss != "" else "",
- convert_bytes(p._swap) if p._swap != "" else "",
- convert_bytes(p._rss),
- )
- print(line)
- if ad_pids:
- print("warning: access denied for %s pids" % (len(ad_pids)),
- file=sys.stderr)
+ procs.sort(key=lambda p: p._uss)
+ templ = "%-7s %-7s %-30s %7s %7s %7s %7s"
+ print(templ % ("PID", "User", "Cmdline", "USS", "PSS", "Swap", "RSS"))
+ print("=" * 78)
+ for p in procs[86:]:
+ line = templ % (
+ p.pid,
+ p._info["username"][:7],
+ " ".join(p._info["cmdline"])[:30],
+ convert_bytes(p._uss),
+ convert_bytes(p._pss) if p._pss != "" else "",
+ convert_bytes(p._swap) if p._swap != "" else "",
+ convert_bytes(p._rss),
+ )
+ print(line)
+ if ad_pids:
+ print("warning: access denied for %s pids" % (len(ad_pids)),
+ file=sys.stderr)
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/scripts/ps.py b/scripts/ps.py
index 8aa3d3f4..b85790d6 100755
--- a/scripts/ps.py
+++ b/scripts/ps.py
@@ -17,6 +17,7 @@ import time
import psutil
+
PROC_STATUSES_RAW = {
psutil.STATUS_RUNNING: "R",
psutil.STATUS_SLEEPING: "S",
diff --git a/scripts/top.py b/scripts/top.py
index 1caa8136..70dbf6c9 100755
--- a/scripts/top.py
+++ b/scripts/top.py
@@ -14,8 +14,8 @@ $ python scripts/top.py
CPU1 [||| ] 7.8%
CPU2 [ ] 2.0%
CPU3 [||||| ] 13.9%
- Mem [||||||||||||||||||| ] 49.8% 4920M/9888M
- Swap [ ] 0.0% 0M/0M
+ Mem [||||||||||||||||||| ] 49.8% 4920M / 9888M
+ Swap [ ] 0.0% 0M / 0M
Processes: 287 (running=1, sleeping=286, zombie=1)
Load average: 0.34 0.54 0.46 Uptime: 3 days, 10:16:37
@@ -48,12 +48,14 @@ import psutil
# --- curses stuff
+
def tear_down():
win.keypad(0)
curses.nocbreak()
curses.echo()
curses.endwin()
+
win = curses.initscr()
atexit.register(tear_down)
curses.endwin()
@@ -75,6 +77,7 @@ def print_line(line, highlight=False):
raise
else:
lineno += 1
+
# --- /curses stuff
@@ -137,11 +140,10 @@ def print_header(procs_status, num_procs):
perc))
mem = psutil.virtual_memory()
dashes, empty_dashes = get_dashes(mem.percent)
- used = mem.total - mem.available
- line = " Mem [%s%s] %5s%% %6s/%s" % (
+ line = " Mem [%s%s] %5s%% %6s / %s" % (
dashes, empty_dashes,
mem.percent,
- str(int(used / 1024 / 1024)) + "M",
+ str(int(mem.used / 1024 / 1024)) + "M",
str(int(mem.total / 1024 / 1024)) + "M"
)
print_line(line)
@@ -149,7 +151,7 @@ def print_header(procs_status, num_procs):
# swap usage
swap = psutil.swap_memory()
dashes, empty_dashes = get_dashes(swap.percent)
- line = " Swap [%s%s] %5s%% %6s/%s" % (
+ line = " Swap [%s%s] %5s%% %6s / %s" % (
dashes, empty_dashes,
swap.percent,
str(int(swap.used / 1024 / 1024)) + "M",
@@ -230,5 +232,6 @@ def main():
except (KeyboardInterrupt, SystemExit):
pass
+
if __name__ == '__main__':
main()
diff --git a/scripts/who.py b/scripts/who.py
index f64c0093..046ec23f 100755
--- a/scripts/who.py
+++ b/scripts/who.py
@@ -29,5 +29,6 @@ def main():
datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"),
user.host))
+
if __name__ == '__main__':
main()
diff --git a/setup.py b/setup.py
index a61b5ed2..ab4fb593 100644..100755
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,24 @@ except ImportError:
HERE = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(HERE, "psutil"))
-import _common # NOQA
+from _common import BSD # NOQA
+from _common import FREEBSD # NOQA
+from _common import LINUX # NOQA
+from _common import NETBSD # NOQA
+from _common import OPENBSD # NOQA
+from _common import OSX # NOQA
+from _common import POSIX # NOQA
+from _common import SUNOS # NOQA
+from _common import WINDOWS # NOQA
+
+
+macros = []
+if POSIX:
+ macros.append(("PSUTIL_POSIX", 1))
+if WINDOWS:
+ macros.append(("PSUTIL_WINDOWS", 1))
+if BSD:
+ macros.append(("PSUTIL_BSD", 1))
def get_version():
@@ -64,25 +81,36 @@ def silenced_output(stream_name):
VERSION = get_version()
-VERSION_MACRO = ('PSUTIL_VERSION', int(VERSION.replace('.', '')))
+macros.append(('PSUTIL_VERSION', int(VERSION.replace('.', ''))))
# POSIX
-if _common.POSIX:
+if POSIX:
posix_extension = Extension(
'psutil._psutil_posix',
sources=['psutil/_psutil_posix.c'])
- if sys.platform.startswith("sunos") or sys.platform.startswith("solaris"):
+ if SUNOS:
posix_extension.libraries.append('socket')
if platform.release() == '5.10':
posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c')
posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1))
+
# Windows
-if _common.WINDOWS:
+if WINDOWS:
def get_winver():
maj, min = sys.getwindowsversion()[0:2]
return '0x0%s' % ((maj * 100) + min)
+ macros.extend([
+ # be nice to mingw, see:
+ # http://www.mingw.org/wiki/Use_more_recent_defined_functions
+ ('_WIN32_WINNT', get_winver()),
+ ('_AVAIL_WINVER_', get_winver()),
+ ('_CRT_SECURE_NO_WARNINGS', None),
+ # see: https://github.com/giampaolo/psutil/issues/348
+ ('PSAPI_VERSION', 1),
+ ])
+
ext = Extension(
'psutil._psutil_windows',
sources=[
@@ -94,16 +122,7 @@ if _common.WINDOWS:
'psutil/arch/windows/inet_ntop.c',
'psutil/arch/windows/services.c',
],
- define_macros=[
- VERSION_MACRO,
- # be nice to mingw, see:
- # http://www.mingw.org/wiki/Use_more_recent_defined_functions
- ('_WIN32_WINNT', get_winver()),
- ('_AVAIL_WINVER_', get_winver()),
- ('_CRT_SECURE_NO_WARNINGS', None),
- # see: https://github.com/giampaolo/psutil/issues/348
- ('PSAPI_VERSION', 1),
- ],
+ define_macros=macros,
libraries=[
"psapi", "kernel32", "advapi32", "shell32", "netapi32",
"iphlpapi", "wtsapi32", "ws2_32",
@@ -112,8 +131,10 @@ if _common.WINDOWS:
# extra_link_args=["/DEBUG"]
)
extensions = [ext]
+
# OS X
-elif _common.OSX:
+elif OSX:
+ macros.append(("PSUTIL_OSX", 1))
ext = Extension(
'psutil._psutil_osx',
sources=[
@@ -121,13 +142,15 @@ elif _common.OSX:
'psutil/_psutil_common.c',
'psutil/arch/osx/process_info.c',
],
- define_macros=[VERSION_MACRO],
+ define_macros=macros,
extra_link_args=[
'-framework', 'CoreFoundation', '-framework', 'IOKit'
])
extensions = [ext, posix_extension]
+
# FreeBSD
-elif _common.FREEBSD:
+elif FREEBSD:
+ macros.append(("PSUTIL_FREEBSD", 1))
ext = Extension(
'psutil._psutil_bsd',
sources=[
@@ -136,11 +159,13 @@ elif _common.FREEBSD:
'psutil/arch/bsd/freebsd.c',
'psutil/arch/bsd/freebsd_socks.c',
],
- define_macros=[VERSION_MACRO],
+ define_macros=macros,
libraries=["devstat"])
extensions = [ext, posix_extension]
+
# OpenBSD
-elif _common.OPENBSD:
+elif OPENBSD:
+ macros.append(("PSUTIL_OPENBSD", 1))
ext = Extension(
'psutil._psutil_bsd',
sources=[
@@ -148,11 +173,13 @@ elif _common.OPENBSD:
'psutil/_psutil_common.c',
'psutil/arch/bsd/openbsd.c',
],
- define_macros=[VERSION_MACRO],
+ define_macros=macros,
libraries=["kvm"])
extensions = [ext, posix_extension]
+
# NetBSD
-elif _common.NETBSD:
+elif NETBSD:
+ macros.append(("PSUTIL_NETBSD", 1))
ext = Extension(
'psutil._psutil_bsd',
sources=[
@@ -161,11 +188,12 @@ elif _common.NETBSD:
'psutil/arch/bsd/netbsd.c',
'psutil/arch/bsd/netbsd_socks.c',
],
- define_macros=[VERSION_MACRO],
+ define_macros=macros,
libraries=["kvm"])
extensions = [ext, posix_extension]
+
# Linux
-elif _common.LINUX:
+elif LINUX:
def get_ethtool_macro():
# see: https://github.com/giampaolo/psutil/issues/659
from distutils.unixccompiler import UnixCCompiler
@@ -192,8 +220,8 @@ elif _common.LINUX:
else:
return None
+ macros.append(("PSUTIL_LINUX", 1))
ETHTOOL_MACRO = get_ethtool_macro()
- macros = [VERSION_MACRO]
if ETHTOOL_MACRO is not None:
macros.append(ETHTOOL_MACRO)
ext = Extension(
@@ -201,14 +229,17 @@ elif _common.LINUX:
sources=['psutil/_psutil_linux.c'],
define_macros=macros)
extensions = [ext, posix_extension]
+
# Solaris
-elif _common.SUNOS:
+elif SUNOS:
+ macros.append(("PSUTIL_SUNOS", 1))
ext = Extension(
'psutil._psutil_sunos',
sources=['psutil/_psutil_sunos.c'],
- define_macros=[VERSION_MACRO],
+ define_macros=macros,
libraries=['kstat', 'nsl', 'socket'])
extensions = [ext, posix_extension]
+
else:
sys.exit('platform %s is not supported' % sys.platform)
@@ -226,7 +257,7 @@ def main():
'monitoring', 'ulimit', 'prlimit', 'smem',
],
author='Giampaolo Rodola',
- author_email='g.rodola <at> gmail <dot> com',
+ author_email='g.rodola@gmail.com',
url='https://github.com/giampaolo/psutil',
platforms='Platform Independent',
license='BSD',
@@ -281,5 +312,6 @@ def main():
setup_args["ext_modules"] = extensions
setup(**setup_args)
+
if __name__ == '__main__':
main()