summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2018-01-01 22:04:16 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2018-01-01 22:04:16 +0100
commit9806c4801cba4c239425226d9db8c5a7f8beb902 (patch)
tree3e7feba9e15c9f08ecfdf4d9714d8dd37311d576
parentc604a55694cc70d54f7abea628d41d99842944cc (diff)
parenta86c6f65c123442802c44d27e45b5e014a62fe3b (diff)
downloadpsutil-1102-proc-bitness.tar.gz
merge from master1102-proc-bitness
-rwxr-xr-x.ci/travis/run.sh2
-rwxr-xr-x.git-pre-commit2
-rw-r--r--.travis.yml16
-rw-r--r--CREDITS44
-rw-r--r--DEVGUIDE.rst63
-rw-r--r--HISTORY.rst127
-rw-r--r--IDEAS1
-rw-r--r--INSTALL.rst59
-rw-r--r--MANIFEST.in19
-rw-r--r--Makefile195
-rw-r--r--README.rst36
-rw-r--r--appveyor.yml11
-rw-r--r--docs/Makefile57
-rw-r--r--docs/README4
-rw-r--r--docs/_static/css/custom.css518
-rw-r--r--docs/_template/globaltoc.html12
-rw-r--r--docs/_template/indexcontent.html4
-rw-r--r--docs/_template/indexsidebar.html8
-rw-r--r--docs/_template/page.html66
-rw-r--r--docs/_themes/pydoctheme/static/pydoctheme.css197
-rw-r--r--docs/_themes/pydoctheme/theme.conf23
-rw-r--r--docs/conf.py235
-rw-r--r--docs/index.rst302
-rw-r--r--psutil/DEVNOTES5
-rw-r--r--psutil/__init__.py299
-rw-r--r--psutil/_common.py5
-rw-r--r--psutil/_exceptions.py94
-rw-r--r--psutil/_psaix.py573
-rw-r--r--psutil/_psbsd.py33
-rw-r--r--psutil/_pslinux.py82
-rw-r--r--psutil/_psosx.py54
-rw-r--r--psutil/_psposix.py12
-rw-r--r--psutil/_pssunos.py20
-rw-r--r--psutil/_psutil_aix.c988
-rw-r--r--psutil/_psutil_bsd.c8
-rw-r--r--psutil/_psutil_common.c99
-rw-r--r--psutil/_psutil_common.h14
-rw-r--r--psutil/_psutil_linux.c8
-rw-r--r--psutil/_psutil_osx.c144
-rw-r--r--psutil/_psutil_posix.c22
-rw-r--r--psutil/_psutil_sunos.c50
-rw-r--r--psutil/_psutil_windows.c196
-rw-r--r--psutil/_pswindows.py11
-rw-r--r--psutil/arch/aix/common.c79
-rw-r--r--psutil/arch/aix/common.h31
-rw-r--r--psutil/arch/aix/ifaddrs.c149
-rw-r--r--psutil/arch/aix/ifaddrs.h35
-rw-r--r--psutil/arch/aix/net_connections.c287
-rw-r--r--psutil/arch/aix/net_connections.h15
-rw-r--r--psutil/arch/aix/net_kernel_structs.h110
-rw-r--r--psutil/arch/freebsd/proc_socks.c38
-rw-r--r--psutil/arch/freebsd/specific.c14
-rw-r--r--psutil/arch/netbsd/specific.c10
-rw-r--r--psutil/arch/openbsd/specific.c10
-rw-r--r--psutil/arch/osx/process_info.c8
-rw-r--r--psutil/arch/solaris/v10/ifaddrs.h4
-rw-r--r--psutil/arch/windows/ntextapi.h12
-rw-r--r--psutil/arch/windows/process_info.c10
-rw-r--r--psutil/tests/__init__.py68
-rwxr-xr-xpsutil/tests/__main__.py12
-rwxr-xr-xpsutil/tests/test_aix.py121
-rwxr-xr-xpsutil/tests/test_connections.py4
-rwxr-xr-xpsutil/tests/test_contracts.py44
-rwxr-xr-xpsutil/tests/test_linux.py46
-rwxr-xr-xpsutil/tests/test_misc.py27
-rwxr-xr-xpsutil/tests/test_osx.py13
-rwxr-xr-xpsutil/tests/test_posix.py62
-rwxr-xr-xpsutil/tests/test_process.py149
-rwxr-xr-xpsutil/tests/test_system.py8
-rwxr-xr-xpsutil/tests/test_unicode.py41
-rwxr-xr-xscripts/ifconfig.py56
-rwxr-xr-xscripts/internal/download_exes.py6
-rwxr-xr-xscripts/internal/generate_manifest.py8
-rwxr-xr-xscripts/internal/print_announce.py9
-rwxr-xr-xscripts/internal/winmake.py67
-rwxr-xr-xscripts/procinfo.py3
-rwxr-xr-xsetup.py27
77 files changed, 4912 insertions, 1389 deletions
diff --git a/.ci/travis/run.sh b/.ci/travis/run.sh
index b27e6695..1501387a 100755
--- a/.ci/travis/run.sh
+++ b/.ci/travis/run.sh
@@ -30,6 +30,6 @@ if [ "$PYVER" == "2.7" ] || [ "$PYVER" == "3.6" ]; then
PSUTIL_TESTING=1 python -Wa psutil/tests/test_memory_leaks.py
# run linter (on Linux only)
if [[ "$(uname -s)" != 'Darwin' ]]; then
- python -Wa -m flake8
+ python -m flake8
fi
fi
diff --git a/.git-pre-commit b/.git-pre-commit
index a2f2d18e..c3c605e0 100755
--- a/.git-pre-commit
+++ b/.git-pre-commit
@@ -75,7 +75,7 @@ def sh(cmd):
def main():
out = sh("git diff --cached --name-only")
- py_files = [x for x in out.split(b'\n') if x.endswith(b'.py') and
+ py_files = [x for x in out.split('\n') if x.endswith('.py') and
os.path.exists(x)]
lineno = 0
diff --git a/.travis.yml b/.travis.yml
index 0f191938..9289eb6b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,31 +3,19 @@ language: python
cache: pip
matrix:
include:
+ # Linux
- python: 2.6
- python: 2.7
- - python: 3.3
- python: 3.4
- python: 3.5
- python: 3.6
- - "pypy"
- # XXX - commented because OSX builds are deadly slow
- # - language: generic
- # os: osx
- # env: PYVER=py26
+ # OSX
- language: generic
os: osx
env: PYVER=py27
- # XXX - commented because OSX builds are deadly slow
- # - language: generic
- # os: osx
- # env: PYVER=py33
- language: generic
os: osx
env: PYVER=py34
- # XXX - not supported yet
- # - language: generic
- # os: osx
- # env: PYVER=py35
install:
- ./.ci/travis/install.sh
script:
diff --git a/CREDITS b/CREDITS
index 77bdfcaf..cf9ce493 100644
--- a/CREDITS
+++ b/CREDITS
@@ -43,6 +43,8 @@ Github usernames of people to CC on github when in need of help.
- SunOS:
- wiggin15, Arnon Yaari
- alxchk, Oleksii Shevchuk
+- AIX:
+ - wiggin15, Arnon Yaari (maintainer)
Contributors
============
@@ -53,6 +55,10 @@ E: jloden@gmail.com
D: original co-author, initial design/bootstrap and occasional bug fixes
W: http://www.jayloden.com
+N: Arnon Yaari (wiggin15)
+W: https://github.com/wiggin15
+I: 517, 607, 610, 1131, 1123, 1130, 1154, 1164, 1174, 1177
+
N: Jeff Tang
W: https://github.com/mrjefftang
I: 340, 529, 616, 653, 654, 648, 641
@@ -363,10 +369,6 @@ N: maozguttman
W: https://github.com/maozguttman
I: 659
-N: wiggin15
-W: https://github.com/wiggin15
-I: 517, 607, 610
-
N: dasumin
W: https://github.com/dasumin
I: 541
@@ -412,8 +414,8 @@ E: khanzf@gmail.com
I: 823
N: Jake Omann
-E: https://github.com/jhomann
-I: 816
+E: https://github.com/jomann09
+I: 816, 775
N: Jeremy Humble
W: https://github.com/jhumble
@@ -443,7 +445,7 @@ I: 919
N: Max BĂ©langer
W: https://github.com/maxbelanger
-I: 936
+I: 936, 1133
N: Pierre Fersing
C: France
@@ -485,3 +487,31 @@ I: 1042, 1079
N: Oleksii Shevchuk
W: https://github.com/alxchk
I: 1077, 1093, 1091
+
+N: Prodesire
+W: https://github.com/Prodesire
+I: 1138
+
+N: Sebastian Saip
+W: https://github.com/ssaip
+I: 1141
+
+N: Jakub Bacic
+W: https://github.com/jakub-bacic
+I: 1127
+
+N: Akos Kiss
+W: https://github.com/akosthekiss
+I: 1150
+
+N: Adrian Page
+W: https://github.com/adpag
+I: 1159, 1160, 1161
+
+N: Matthew Long
+W: https://github.com/matray
+I: 1167
+
+N: janderbrain
+W: https://github.com/janderbrain
+I: 1169
diff --git a/DEVGUIDE.rst b/DEVGUIDE.rst
index 6a0f08fc..2d48ced2 100644
--- a/DEVGUIDE.rst
+++ b/DEVGUIDE.rst
@@ -4,17 +4,23 @@ Setup and running tests
If you plan on hacking on psutil this is what you're supposed to do first:
-- clone the GIT repository::
+- clone the GIT repository:
+
+.. code-block:: bash
$ git clone git@github.com:giampaolo/psutil.git
-- install test deps and GIT hooks::
+- install test deps and GIT hooks:
+
+.. code-block:: bash
+
+ make setup-dev-env
- $ make setup-dev-env
+- run tests:
-- run tests::
+.. code-block:: bash
- $ make test
+ make test
- bear in mind that ``make``
(see `Makefile <https://github.com/giampaolo/psutil/blob/master/Makefile>`_)
@@ -23,6 +29,7 @@ If you plan on hacking on psutil this is what you're supposed to do first:
(see `make.bat <https://github.com/giampaolo/psutil/blob/master/make.bat>`_).
- do not use ``sudo``; ``make install`` installs psutil as a limited user in
"edit" mode; also ``make setup-dev-env`` installs deps as a limited user.
+- use `make help` to see the list of available commands.
============
Coding style
@@ -37,34 +44,46 @@ Coding style
Makefile
========
-Some useful make commands::
+Some useful make commands:
+
+.. code-block:: bash
- $ make install # install
- $ make setup-dev-env # install useful dev libs (pyflakes, unittest2, etc.)
- $ make test # run unit tests
- $ make test-memleaks # run memory leak tests
- $ make coverage # run test coverage
- $ make flake8 # run PEP8 linter
+ make install # install
+ make setup-dev-env # install useful dev libs (pyflakes, unittest2, etc.)
+ make test # run unit tests
+ make test-memleaks # run memory leak tests
+ make test-coverage # run test coverage
+ make flake8 # run PEP8 linter
There are some differences between ``make`` on UNIX and Windows.
-For instance, to run a specific Python version. On UNIX::
+For instance, to run a specific Python version. On UNIX:
+
+.. code-block:: bash
make test PYTHON=python3.5
-On Windows::
+On Windows:
+
+.. code-block:: bat
set PYTHON=C:\python35\python.exe && make test
- # ...or:
+...or:
+
+.. code-block:: bat
make -p 35 test
If you want to modify psutil and run a script on the fly which uses it do
-(on UNIX)::
+(on UNIX):
+
+.. code-block:: bash
make test TSCRIPT=foo.py
-On Windows::
+On Windows:
+
+.. code-block:: bat
set TSCRIPT=foo.py && make test
@@ -74,7 +93,7 @@ Adding a new feature
Usually the files involved when adding a new functionality are:
-.. code-block:: plain
+.. code-block:: bash
psutil/__init__.py # main psutil namespace
psutil/_ps{platform}.py # python platform wrapper
@@ -142,7 +161,7 @@ Two icons in the home page (README) always show the build status:
:target: https://ci.appveyor.com/project/giampaolo/psutil
:alt: Windows tests (Appveyor)
-OSX, BSD and Solaris are currently tested manually (sigh!).
+OSX, BSD, AIX and Solaris are currently tested manually (sigh!).
Test coverage
-------------
@@ -164,7 +183,7 @@ Documentation
- it uses `RsT syntax <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_
and it's built with `sphinx <http://sphinx-doc.org/>`_.
- doc can be built with ``make setup-dev-env; cd docs; make html``.
-- public doc is hosted on http://pythonhosted.org/psutil/
+- public doc is hosted on http://psutil.readthedocs.io/
=======================
Releasing a new version
@@ -184,7 +203,7 @@ FreeBSD notes
.. code-block:: bash
- $ pkg install python python3 gcc git vim screen bash
- $ chsh -s /usr/local/bin/bash user # set bash as default shell
+ pkg install python python3 gcc git vim screen bash
+ chsh -s /usr/local/bin/bash user # set bash as default shell
- ``/usr/src`` contains the source codes for all installed CLI tools (grep in it).
diff --git a/HISTORY.rst b/HISTORY.rst
index aa9628b2..dd813c5b 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,10 +1,113 @@
*Bug tracker at https://github.com/giampaolo/psutil/issues*
-*XXXX-XX-XX*
+5.4.3
+=====
+
+*2018-01-01*
+
+**Enhancements**
+
+- 775_: disk_partitions() on Windows return mount points.
+
+**Bug fixes**
+
+- 1193_: pids() may return False on OSX.
+
+5.4.2
+=====
+
+*2017-12-07*
+
+**Enhancements**
+
+- 1173_: introduced PSUTIL_DEBUG environment variable which can be set in order
+ to print useful debug messages on stderr (useful in case of nasty errors).
+- 1177_: added support for sensors_battery() on OSX. (patch by Arnon Yaari)
+- 1183_: Process.children() is 2x faster on UNIX and 2.4x faster on Linux.
+- 1188_: deprecated method Process.memory_info_ex() now warns by using
+ FutureWarning instead of DeprecationWarning.
+
+**Bug fixes**
+
+- 1152_: [Windows] disk_io_counters() may return an empty dict.
+- 1169_: [Linux] users() "hostname" returns username instead. (patch by
+ janderbrain)
+- 1172_: [Windows] `make test` does not work.
+- 1179_: [Linux] Process.cmdline() is now able to splits cmdline args for
+ misbehaving processes which overwrite /proc/pid/cmdline and use spaces
+ instead of null bytes as args separator.
+- 1181_: [OSX] Process.memory_maps() may raise ENOENT.
+- 1187_: [OSX] pids() does not return PID 0 on recent OSX versions.
+
+5.4.1
+=====
+
+*2017-11-08*
+
+**Enhancements**
+
+- 1164_: [AIX] add support for Process.num_ctx_switches(). (patch by Arnon
+ Yaari)
+- 1053_: abandon Python 3.3 support (psutil still works but it's no longer
+ tested).
+
+**Bug fixes**
+
+- 1150_: [Windows] when a process is terminate()d now the exit code is set to
+ SIGTERM instead of 0. (patch by Akos Kiss)
+- 1151_: python -m psutil.tests fail
+- 1154_: [AIX] psutil won't compile on AIX 6.1.0. (patch by Arnon Yaari)
+- 1167_: [Windows] net_io_counter() packets count now include also non-unicast
+ packets. (patch by Matthew Long)
+
+5.4.0
+=====
+
+*2017-10-12*
+
+**Enhancements**
+
+- 1123_: [AIX] added support for AIX platform. (patch by Arnon Yaari)
+
+**Bug fixes**
+
+- 1009_: [Linux] sensors_temperatures() may crash with IOError.
+- 1012_: [Windows] disk_io_counters()'s read_time and write_time were expressed
+ in tens of micro seconds instead of milliseconds.
+- 1127_: [OSX] invalid reference counting in Process.open_files() may lead to
+ segfault. (patch by Jakub Bacic)
+- 1129_: [Linux] sensors_fans() may crash with IOError. (patch by Sebastian
+ Saip)
+- 1131_: [SunOS] fix compilation warnings. (patch by Arnon Yaari)
+- 1133_: [Windows] can't compile on newer versions of Visual Studio 2017 15.4.
+ (patch by Max BĂ©langer)
+- 1138_: [Linux] can't compile on CentOS 5.0 and RedHat 5.0.
+ (patch by Prodesire)
+
+5.3.1
+=====
+
+*2017-09-10*
+
+**Enhancements**
+
+- 1124_: documentation moved to http://psutil.readthedocs.io
+
+**Bug fixes**
+
+- 1105_: [FreeBSD] psutil does not compile on FreeBSD 12.
+- 1125_: [BSD] net_connections() raises TypeError.
+
+**Compatibility notes**
+
+- 1120_: .exe files for Windows are no longer uploaded on PYPI as per PEP-527;
+ only wheels are provided.
5.3.0
=====
+*2017-09-01*
+
**Enhancements**
- 802_: disk_io_counters() and net_io_counters() numbers no longer wrap
@@ -65,7 +168,7 @@
- 1063_: [NetBSD] net_connections() may list incorrect sockets.
- 1064_: [NetBSD] swap_memory() may segfault in case of error.
- 1065_: [OpenBSD] Process.cmdline() may raise SystemError.
-- 1067_: [NetBSD] Process.cmdline() leaks memory if proces has terminated.
+- 1067_: [NetBSD] Process.cmdline() leaks memory if process has terminated.
- 1069_: [FreeBSD] Process.cpu_num() may return 255 for certain kernel
processes.
- 1071_: [Linux] cpu_freq() may raise IOError on old RedHat distros.
@@ -101,16 +204,16 @@
- 1040_: the following Windows APIs on Python 2 now return a string instead of
unicode:
- Process.memory_maps().path
- - WindosService.bin_path()
- - WindosService.description()
- - WindosService.display_name()
- - WindosService.username()
-
-*2017-04-10*
+ - WindowsService.bin_path()
+ - WindowsService.description()
+ - WindowsService.display_name()
+ - WindowsService.username()
5.2.2
=====
+*2017-04-10*
+
**Bug fixes**
- 1000_: fixed some setup.py warnings.
@@ -122,11 +225,11 @@
- 1009_: [Linux] sensors_temperatures() may raise OSError.
- 1010_: [Linux] virtual_memory() may raise ValueError on Ubuntu 14.04.
-*2017-03-24*
-
5.2.1
=====
+*2017-03-24*
+
**Bug fixes**
- 981_: [Linux] cpu_freq() may return an empty list.
@@ -136,11 +239,11 @@
- 997_: [FreeBSD] virtual_memory() may fail due to missing sysctl parameter on
FreeBSD 12.
-*2017-03-05*
-
5.2.0
=====
+*2017-03-05*
+
**Enhancements**
- 971_: [Linux] Add psutil.sensors_fans() function. (patch by Nicolas Hennion)
diff --git a/IDEAS b/IDEAS
index d122be87..4932ad72 100644
--- a/IDEAS
+++ b/IDEAS
@@ -9,7 +9,6 @@ PLATFORMS
=========
- #355: Android (with patch)
-- #605: AIX (with branch)
- #82: Cygwin (PR at #998)
- #276: GNU/Hurd
- #693: Windows Nano
diff --git a/INSTALL.rst b/INSTALL.rst
index 8737c94a..f2a80eed 100644
--- a/INSTALL.rst
+++ b/INSTALL.rst
@@ -4,16 +4,22 @@ Install pip
pip is the easiest way to install psutil.
It is shipped by default with Python 2.7.9+ and 3.4+. For other Python versions
you can install it manually.
-On Linux or via wget::
+On Linux or via wget:
+
+.. code-block:: bash
wget https://bootstrap.pypa.io/get-pip.py -O - | python
-On OSX or via curl::
+On OSX or via curl:
+
+.. code-block:: bash
python < <(curl -s https://bootstrap.pypa.io/get-pip.py)
On Windows, `download pip <https://pip.pypa.io/en/latest/installing/>`__, open
-cmd.exe and install it::
+cmd.exe and install it:
+
+.. code-block:: bat
C:\Python27\python.exe get-pip.py
@@ -25,50 +31,43 @@ If you're not or you bump into permission errors you can either:
* prepend ``sudo``, e.g.:
-::
+.. code-block:: bash
sudo pip install psutil
* install psutil for your user only (not at system level):
-::
+.. code-block:: bash
pip install --user psutil
Linux
=====
-Ubuntu / Debian::
+Ubuntu / Debian:
+
+.. code-block:: bash
sudo apt-get install gcc python-dev python-pip
pip install psutil
-RedHat / CentOS::
+RedHat / CentOS:
+
+
+.. code-block:: bash
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
===
Install `Xcode <https://developer.apple.com/downloads/?name=Xcode>`__
first, then:
-::
+.. code-block:: bash
pip install psutil
@@ -77,7 +76,9 @@ Windows
The easiest way to install psutil on Windows is to just use the pre-compiled
exe/wheel installers hosted on
-`PYPI <https://pypi.python.org/pypi/psutil/#downloads>`__ via pip::
+`PYPI <https://pypi.python.org/pypi/psutil/#downloads>`__ via pip:
+
+.. code-block:: bat
C:\Python27\python.exe -m pip install psutil
@@ -92,7 +93,9 @@ Compiling 64 bit versions of Python 2.6 and 2.7 with VS 2008 requires
`Windows SDK and .NET Framework 3.5 SP1 <https://www.microsoft.com/en-us/download/details.aspx?id=3138>`__.
Once installed run vcvars64.bat, then you can finally compile (see
`here <http://stackoverflow.com/questions/11072521/>`__).
-To compile / install psutil from sources on Windows run::
+To compile / install psutil from sources on Windows run:
+
+.. code-block:: bat
make.bat build
make.bat install
@@ -100,7 +103,7 @@ To compile / install psutil from sources on Windows run::
FreeBSD
=======
-::
+.. code-block:: bash
pkg install python gcc
python -m pip install psutil
@@ -108,7 +111,7 @@ FreeBSD
OpenBSD
=======
-::
+.. code-block:: bash
export PKG_PATH="http://ftp.openbsd.org/pub/OpenBSD/`uname -r`/packages/`arch -s`/"
pkg_add -v python gcc
@@ -117,7 +120,7 @@ OpenBSD
NetBSD
======
-::
+.. code-block:: bash
export PKG_PATH="ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/`uname -m`/`uname -r`/All"
pkg_add -v pkgin
@@ -129,13 +132,13 @@ Solaris
If ``cc`` compiler is not installed create a symlink to ``gcc``:
-::
+.. code-block:: bash
sudo ln -s /usr/bin/gcc /usr/local/bin/cc
Install:
-::
+.. code-block:: bash
pkg install gcc
python -m pip install psutil
@@ -143,7 +146,7 @@ Install:
Install from sources
====================
-::
+.. code-block:: bash
git clone https://github.com/giampaolo/psutil.git
cd psutil
diff --git a/MANIFEST.in b/MANIFEST.in
index 89c42216..7a92a4e5 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -13,26 +13,25 @@ include README.rst
include docs/Makefile
include docs/README
include docs/_static/copybutton.js
+include docs/_static/css/custom.css
include docs/_static/favicon.ico
include docs/_static/sidebar.js
-include docs/_template/globaltoc.html
-include docs/_template/indexcontent.html
-include docs/_template/indexsidebar.html
-include docs/_template/page.html
-include docs/_themes/pydoctheme/static/pydoctheme.css
-include docs/_themes/pydoctheme/theme.conf
include docs/conf.py
include docs/index.rst
include docs/make.bat
include make.bat
+include psutil/DEVNOTES
include psutil/__init__.py
include psutil/_common.py
include psutil/_compat.py
+include psutil/_exceptions.py
+include psutil/_psaix.py
include psutil/_psbsd.py
include psutil/_pslinux.py
include psutil/_psosx.py
include psutil/_psposix.py
include psutil/_pssunos.py
+include psutil/_psutil_aix.c
include psutil/_psutil_bsd.c
include psutil/_psutil_common.c
include psutil/_psutil_common.h
@@ -43,6 +42,13 @@ include psutil/_psutil_posix.h
include psutil/_psutil_sunos.c
include psutil/_psutil_windows.c
include psutil/_pswindows.py
+include psutil/arch/aix/common.c
+include psutil/arch/aix/common.h
+include psutil/arch/aix/ifaddrs.c
+include psutil/arch/aix/ifaddrs.h
+include psutil/arch/aix/net_connections.c
+include psutil/arch/aix/net_connections.h
+include psutil/arch/aix/net_kernel_structs.h
include psutil/arch/freebsd/proc_socks.c
include psutil/arch/freebsd/proc_socks.h
include psutil/arch/freebsd/specific.c
@@ -76,6 +82,7 @@ include psutil/arch/windows/services.h
include psutil/tests/README.rst
include psutil/tests/__init__.py
include psutil/tests/__main__.py
+include psutil/tests/test_aix.py
include psutil/tests/test_bsd.py
include psutil/tests/test_connections.py
include psutil/tests/test_contracts.py
diff --git a/Makefile b/Makefile
index f47d262c..5081a4ed 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,6 @@
PYTHON = python
TSCRIPT = psutil/tests/__main__.py
ARGS =
-
# List of nice-to-have dev libs.
DEPS = \
argparse \
@@ -23,10 +22,11 @@ DEPS = \
sphinx \
twine \
unittest2 \
- requests
+ wheel
# 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')"`
+TEST_PREFIX = PYTHONWARNINGS=all PSUTIL_TESTING=1 PSUTIL_DEBUG=1
all: test
@@ -34,8 +34,7 @@ all: test
# Install
# ===================================================================
-# Remove all build files.
-clean:
+clean: ## Remove all build files.
rm -rf `find . -type d -name __pycache__ \
-o -type f -name \*.bak \
-o -type f -name \*.orig \
@@ -60,9 +59,7 @@ clean:
_:
-
-# Compile without installing.
-build: _
+build: _ ## Compile without installing.
# make sure setuptools is installed (needed for 'develop' / edit mode)
$(PYTHON) -c "import setuptools"
PYTHONWARNINGS=all $(PYTHON) setup.py build
@@ -73,21 +70,16 @@ build: _
rm -rf tmp
$(PYTHON) -c "import psutil" # make sure it actually worked
-# 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:
+install: ## Install this package as current user in "edit" mode.
${MAKE} build
PYTHONWARNINGS=all $(PYTHON) setup.py develop $(INSTALL_OPTS)
rm -rf tmp
-# Uninstall this package via pip.
-uninstall:
+uninstall: ## Uninstall this package via pip.
cd ..; $(PYTHON) -m pip uninstall -y -v psutil
-# Install PIP (only if necessary).
-install-pip:
- PYTHONWARNINGS=all $(PYTHON) -c \
+install-pip: ## Install pip (no-op if already installed).
+ $(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'; \
@@ -105,8 +97,7 @@ install-pip:
f.close(); \
sys.exit(code);"
-# Install GIT hooks, pip, test deps (also upgrades them).
-setup-dev-env:
+setup-dev-env: ## Install GIT hooks, pip, test deps (also upgrades them).
${MAKE} install-git-hooks
${MAKE} install-pip
$(PYTHON) -m pip install $(INSTALL_OPTS) --upgrade pip
@@ -116,68 +107,55 @@ setup-dev-env:
# Tests
# ===================================================================
-# Run all tests.
-test:
+test: ## Run all tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) $(TSCRIPT)
+ $(TEST_PREFIX) $(PYTHON) $(TSCRIPT)
-# Run process-related API tests.
-test-process:
+test-process: ## Run process-related API tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_process
+ $(TEST_PREFIX) $(PYTHON) -m unittest -v psutil.tests.test_process
-# Run system-related API tests.
-test-system:
+test-system: ## Run system-related API tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_system
+ $(TEST_PREFIX) $(PYTHON) -m unittest -v psutil.tests.test_system
-# Run miscellaneous tests.
-test-misc:
+test-misc: ## Run miscellaneous tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_misc.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_misc.py
-# Test APIs dealing with strings.
-test-unicode:
+test-unicode: ## Test APIs dealing with strings.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_unicode.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_unicode.py
-# APIs sanity tests.
-test-contracts:
+test-contracts: ## APIs sanity tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_contracts.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_contracts.py
-# Test net_connections() and Process.connections().
-test-connections:
+test-connections: ## Test net_connections() and Process.connections().
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_connections.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_connections.py
-# POSIX specific tests.
-test-posix:
+test-posix: ## POSIX specific tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_posix.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_posix.py
-# Run specific platform tests only.
-test-platform:
+test-platform: ## Run specific platform tests only.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(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
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS", "AIX") if getattr(psutil, x)][0])'`.py
-# Memory leak tests.
-test-memleaks:
+test-memleaks: ## Memory leak tests.
${MAKE} install
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_memory_leaks.py
+ $(TEST_PREFIX) $(PYTHON) psutil/tests/test_memory_leaks.py
-# Run a specific test by name, e.g.
-# make test-by-name psutil.tests.test_system.TestSystemAPIs.test_cpu_times
-test-by-name:
+test-by-name: ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSystemAPIs
${MAKE} install
- @PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v $(ARGS)
+ @$(TEST_PREFIX) $(PYTHON) -m unittest -v $(ARGS)
-# Run test coverage.
-coverage:
+test-coverage: ## Run test coverage.
${MAKE} install
# Note: coverage options are controlled by .coveragerc file
rm -rf .coverage htmlcov
- PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m coverage run $(TSCRIPT)
+ $(TEST_PREFIX) $(PYTHON) -m coverage run $(TSCRIPT)
$(PYTHON) -m coverage report
@echo "writing results to htmlcov/index.html"
$(PYTHON) -m coverage html
@@ -187,27 +165,25 @@ coverage:
# Linters
# ===================================================================
-pep8:
+pep8: ## PEP8 linter.
@git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8
-pyflakes:
+pyflakes: ## Pyflakes linter.
@export PYFLAKES_NODOCTEST=1 && \
git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes
-flake8:
+flake8: ## flake8 linter.
@git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8
# ===================================================================
# GIT
# ===================================================================
-# git-tag a new release
-git-tag-release:
+git-tag-release: ## Git-tag a new 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-hooks:
+install-git-hooks: ## Install GIT pre-commit hook.
ln -sf ../../.git-pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
@@ -215,86 +191,79 @@ install-git-hooks:
# Distribution
# ===================================================================
-# Generate tar.gz source distribution.
-sdist:
- ${MAKE} clean
+# --- create
+
+sdist: ## Create tar.gz source distribution.
${MAKE} generate-manifest
- PYTHONWARNINGS=all $(PYTHON) setup.py sdist
+ $(PYTHON) setup.py sdist
-# Upload source tarball on https://pypi.python.org/pypi/psutil.
-upload-src:
- ${MAKE} sdist
- PYTHONWARNINGS=all $(PYTHON) setup.py sdist upload
+wheel: ## Generate wheel.
+ $(PYTHON) setup.py bdist_wheel
-# Download exes/wheels hosted on appveyor.
-win-download-exes:
- PYTHONWARNINGS=all $(PYTHON) scripts/internal/download_exes.py --user giampaolo --project psutil
+win-download-wheels: ## Download wheels hosted on appveyor.
+ $(TEST_PREFIX) $(PYTHON) scripts/internal/download_exes.py --user giampaolo --project psutil
-# Upload exes/wheels in dist/* directory to PYPI.
-win-upload-exes:
- PYTHONWARNINGS=all $(PYTHON) -m twine upload dist/*.exe
- PYTHONWARNINGS=all $(PYTHON) -m twine upload dist/*.whl
+# --- upload
-# All the necessary steps before making a release.
-pre-release:
- @PYTHONWARNINGS=all $(PYTHON) -c "import subprocess, sys; out = subprocess.check_output('git diff-index HEAD --', shell=True).strip(); sys.exit('there are uncommitted changes') if out else sys.exit(0);"
+upload-src: ## Upload source tarball on https://pypi.python.org/pypi/psutil.
${MAKE} sdist
+ $(PYTHON) setup.py sdist upload
+
+upload-win-wheels: ## Upload wheels in dist/* directory on PYPI.
+ $(PYTHON) -m twine upload dist/*.whl
+
+# --- others
+
+pre-release: ## Check if we're ready to produce a new release.
+ rm -rf dist
${MAKE} install
- @PYTHONWARNINGS=all $(PYTHON) -c \
+ ${MAKE} generate-manifest
+ git diff MANIFEST.in > /dev/null # ...otherwise 'git diff-index HEAD' will complain
+ ${MAKE} win-download-wheels
+ ${MAKE} sdist
+ $(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, 'XXXX in HISTORY.rst';"
- ${MAKE} win-download-exes
+ $(PYTHON) -c "import subprocess, sys; out = subprocess.check_output('git diff --quiet && git diff --cached --quiet', shell=True).strip(); sys.exit('there are uncommitted changes:\n%s' % out) if out else 0 ;"
-# Create a release: creates tar.gz and exes/wheels, uploads them,
-# upload doc, git tag release.
-release:
+release: ## Create a release (down/uploads tar.gz, wheels, git tag release).
${MAKE} pre-release
- PYTHONWARNINGS=all $(PYTHON) -m twine upload dist/* # upload tar.gz, exes, wheels on PYPI
+ $(PYTHON) -m twine upload dist/* # upload tar.gz and Windows wheels on PYPI
${MAKE} git-tag-release
-# Print announce of new release.
-print-announce:
- @PYTHONWARNINGS=all $(PYTHON) scripts/internal/print_announce.py
+print-announce: ## Print announce of new release.
+ @$(TEST_PREFIX) $(PYTHON) scripts/internal/print_announce.py
-# Print releases' timeline.
-print-timeline:
- @PYTHONWARNINGS=all $(PYTHON) scripts/internal/print_timeline.py
+print-timeline: ## Print releases' timeline.
+ @$(TEST_PREFIX) $(PYTHON) scripts/internal/print_timeline.py
-# Inspect MANIFEST.in file.
-check-manifest:
- PYTHONWARNINGS=all $(PYTHON) -m check_manifest -v $(ARGS)
+check-manifest: ## Inspect MANIFEST.in file.
+ $(PYTHON) -m check_manifest -v $(ARGS)
-# Generates MANIFEST.in file.
-generate-manifest:
- @PYTHONWARNINGS=all $(PYTHON) scripts/internal/generate_manifest.py > MANIFEST.in
+generate-manifest: ## Generates MANIFEST.in file.
+ $(PYTHON) scripts/internal/generate_manifest.py > MANIFEST.in
# ===================================================================
# Misc
# ===================================================================
-grep-todos:
+grep-todos: ## Look for TODOs in the source files.
git grep -EIn "TODO|FIXME|XXX"
-# run script which benchmarks oneshot() ctx manager (see #799)
-bench-oneshot:
+bench-oneshot: ## Benchmarks for oneshot() ctx manager (see #799).
${MAKE} install
- PYTHONWARNINGS=all $(PYTHON) scripts/internal/bench_oneshot.py
+ $(TEST_PREFIX) $(PYTHON) scripts/internal/bench_oneshot.py
-# same as above but using perf module (supposed to be more precise)
-bench-oneshot-2:
+bench-oneshot-2: ## Same as above but using perf module (supposed to be more precise)
${MAKE} install
- PYTHONWARNINGS=all $(PYTHON) scripts/internal/bench_oneshot_2.py
+ $(TEST_PREFIX) $(PYTHON) scripts/internal/bench_oneshot_2.py
-# generate a doc.zip file and manually upload it to PYPI.
-doc:
- cd docs && make html && cd _build/html/ && zip doc.zip -r .
- mv docs/_build/html/doc.zip .
- @echo "done; now manually upload doc.zip from here: https://pypi.python.org/pypi?:action=pkg_edit&name=psutil"
-
-# check whether the links mentioned in some files are valid.
-check-broken-links:
+check-broken-links: ## Look for broken links in source files.
git ls-files | xargs $(PYTHON) -Wa scripts/internal/check_broken_links.py
+
+help: ## Display callable targets.
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
diff --git a/README.rst b/README.rst
index 11898c47..4ac707ab 100644
--- a/README.rst
+++ b/README.rst
@@ -10,6 +10,10 @@
:target: https://coveralls.io/github/giampaolo/psutil?branch=master
:alt: Test coverage (coverall.io)
+.. image:: https://readthedocs.org/projects/psutil/badge/?version=latest
+ :target: http://psutil.readthedocs.io/en/latest/?badge=latest
+ :alt: Documentation Status
+
.. image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
:target: https://pypi.python.org/pypi/psutil/
:alt: Latest version
@@ -28,7 +32,7 @@ Quick links
- `Home page <https://github.com/giampaolo/psutil>`_
- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
-- `Documentation <http://pythonhosted.org/psutil/>`_
+- `Documentation <http://psutil.readthedocs.io>`_
- `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`_
- `Forum <http://groups.google.com/group/psutil/topics>`_
- `Blog <http://grodola.blogspot.com/search/label/psutil>`_
@@ -44,14 +48,20 @@ retrieving information on **running processes** and **system utilization**
(CPU, memory, disks, network, sensors) 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:
+It implements many functionalities offered by UNIX 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.6** (users of Python 2.4 and 2.5 may use
-`2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+psutil currently supports the following platforms:
+
+- **Linux**
+- **Windows**
+- **OSX**,
+- **FreeBSD, OpenBSD**, **NetBSD**
+- **Sun Solaris**
+- **AIX**
+
+...both **32-bit** and **64-bit** architectures, with Python
+versions from **2.6 to 3.6**.
`PyPy <http://pypy.org/>`__ is also known to work.
====================
@@ -67,14 +77,16 @@ Example applications
+------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------+
Also see `scripts directory <https://github.com/giampaolo/psutil/tree/master/scripts>`__
-and `doc recipes <https://pythonhosted.org/psutil/#recipes/>`__.
+and `doc recipes <http://psutil.readthedocs.io/#recipes/>`__.
=====================
Projects using psutil
=====================
-At the time of writing there are over
-`5400 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
+At the time of writing psutil has roughly
+`2.9 milion downloads <https://github.com/giampaolo/psutil/issues/1053#issuecomment-340166262>`__
+per month and there are over
+`6000 open source projects <https://libraries.io/pypi/psutil/dependent_repositories?page=1>`__
on github which depend from psutil.
Here's some I find particularly interesting:
@@ -83,7 +95,7 @@ Here's some I find particularly interesting:
- https://github.com/google/grr
- https://github.com/Jahaja/psdash
- https://github.com/ajenti/ajenti
-
+- https://github.com/home-assistant/home-assistant/
========
Portings
@@ -438,7 +450,7 @@ Windows services
Other samples
=============
-See `doc recipes <https://pythonhosted.org/psutil/#recipes/>`__.
+See `doc recipes <http://psutil.readthedocs.io/#recipes>`__.
======
Author
diff --git a/appveyor.yml b/appveyor.yml
index d4671784..092dc23a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -20,10 +20,6 @@ environment:
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "32"
- - PYTHON: "C:\\Python33"
- PYTHON_VERSION: "3.3.x"
- PYTHON_ARCH: "32"
-
- PYTHON: "C:\\Python34"
PYTHON_VERSION: "3.4.x"
PYTHON_ARCH: "32"
@@ -42,10 +38,6 @@ environment:
PYTHON_VERSION: "2.7.x"
PYTHON_ARCH: "64"
- - PYTHON: "C:\\Python33-x64"
- PYTHON_VERSION: "3.3.x"
- PYTHON_ARCH: "64"
-
- PYTHON: "C:\\Python34-x64"
PYTHON_VERSION: "3.4.x"
PYTHON_ARCH: "64"
@@ -91,11 +83,10 @@ build: off
test_script:
- "%WITH_COMPILER% %PYTHON%/python -V"
- - "set PSUTIL_TESTING=1 && %WITH_COMPILER% %PYTHON%/python psutil/tests/__main__.py"
+ - "set PYTHONWARNINGS=all && set PSUTIL_TESTING=1 && set PSUTIL_DEBUG=1 && %WITH_COMPILER% %PYTHON%/python psutil/tests/__main__.py"
after_test:
- "%WITH_COMPILER% %PYTHON%/python setup.py bdist_wheel"
- - "%WITH_COMPILER% %PYTHON%/python setup.py bdist_wininst"
artifacts:
- path: dist\*
diff --git a/docs/Makefile b/docs/Makefile
index a69fc329..0c4bdf48 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -15,8 +15,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
+.PHONY: help
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@@ -26,8 +25,10 @@ help:
@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 " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@@ -41,41 +42,51 @@ help:
@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)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+.PHONY: clean
clean:
rm -rf $(BUILDDIR)
+.PHONY: html
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+.PHONY: dirhtml
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+.PHONY: singlehtml
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+.PHONY: pickle
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
+.PHONY: json
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
+.PHONY: htmlhelp
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
+.PHONY: qthelp
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@@ -85,6 +96,16 @@ qthelp:
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/psutil.qhc"
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@@ -94,11 +115,19 @@ devhelp:
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/psutil"
@echo "# devhelp"
+.PHONY: epub
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@@ -106,28 +135,33 @@ latex:
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
+.PHONY: latexpdf
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+.PHONY: latexpdfja
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+.PHONY: text
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
+.PHONY: man
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+.PHONY: texinfo
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@@ -135,39 +169,58 @@ texinfo:
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
+.PHONY: info
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+.PHONY: gettext
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+.PHONY: changes
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
+.PHONY: linkcheck
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
+.PHONY: doctest
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+.PHONY: pseudoxml
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/docs/README b/docs/README
index 3aaea8a5..8ceb5f21 100644
--- a/docs/README
+++ b/docs/README
@@ -3,7 +3,7 @@ About
This directory contains the reStructuredText (reST) sources to the psutil
documentation. You don't need to build them yourself, prebuilt versions are
-available at https://pythonhosted.org/psutil/.
+available at http://psutil.readthedocs.io.
In case you want, you need to install sphinx first:
$ pip install sphinx
@@ -12,4 +12,4 @@ Then run:
$ make html
-You'll then have an HTML version of the doc at _build/html/index.html. \ No newline at end of file
+You'll then have an HTML version of the doc at _build/html/index.html.
diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css
new file mode 100644
index 00000000..b76f442a
--- /dev/null
+++ b/docs/_static/css/custom.css
@@ -0,0 +1,518 @@
+.wy-nav-content {
+ max-width: 100% !important;
+ padding: 15px !important;
+}
+
+.rst-content dl:not(.docutils) {
+ margin: 0px 0px 0px 0px;
+}
+
+.data dd {
+ margin-bottom: 0px !important;
+}
+
+.data .descname {
+ border-right:10px !important;
+}
+
+.local-toc li ul li{
+ padding-left: 20px !important;
+}
+
+.function .descclassname {
+ font-weight: normal !important;
+}
+
+.class .descclassname {
+ font-weight: normal !important;
+}
+
+.admonition.warning {
+ padding-top: 2px !important;
+ padding-bottom: 2px !important;
+}
+
+.admonition.note {
+ padding-top: 2px !important;
+ padding-bottom: 2px !important;
+}
+
+.rst-content dl:not(.docutils) dt {
+ color: #555;
+}
+
+.sig-paren {
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+h1, h2, h3 {
+ background: #eee;
+ padding: 5px;
+ border-bottom: 1px solid #ccc;
+}
+
+h1 {
+ font-size: 35px;
+}
+
+.admonition.warning {
+ padding-top: 5px !important;
+ padding-bottom: 5px !important;
+}
+
+.admonition.warning p {
+ margin-bottom: 5px !important;
+}
+
+.admonition.note {
+ padding-top: 5px !important;
+ padding-bottom: 5px !important;
+}
+
+.admonition.note p {
+ margin-bottom: 5px !important;
+ backround-color: rgb(238, 255, 204) !important;
+}
+
+.codeblock div[class^='highlight'], pre.literal-block div[class^='highlight'], .rst-content .literal-block div[class^='highlight'], div[class^='highlight'] div[class^='highlight'] {
+ background-color: #eeffcc !important;
+}
+
+.highlight .hll {
+ background-color: #ffffcc
+}
+
+.highlight {
+ background: #eeffcc;
+}
+
+.highlight-default, .highlight-python {
+ border-radius: 3px !important;
+ border: 1px solid #ac9 !important;
+}
+
+.highlight .c {
+ color: #408090;
+ font-style: italic
+}
+
+.wy-side-nav-search {
+ background-color: grey !important
+}
+
+.highlight {
+ border-radius: 3px !important;
+
+}
+
+div.highlight-default {
+ margin-bottom: 10px !important;
+}
+
+pre {
+ padding: 5px !important;
+}
+
+/* ================================================================== */
+/* Warnings and info boxes like python doc */
+/* ================================================================== */
+
+div.admonition {
+ margin-top: 10px !important;
+ margin-bottom: 10px !important;
+}
+
+div.warning {
+ background-color: #ffe4e4 !important;
+ border: 1px solid #f66 !important;
+ border-radius: 3px !important;
+}
+
+div.note {
+ background-color: #eee !important;
+ border: 1px solid #ccc !important;
+ border-radius: 3px !important;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline !important;
+}
+
+p.admonition-title {
+ display: inline !important;
+ background: none !important;
+ color: black !important;
+}
+
+p.admonition-title:after {
+ content: ":" !important;
+}
+
+div.body div.admonition, div.body div.impl-detail {
+}
+
+.fa-exclamation-circle:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .rst-content .admonition-title:before {
+ display: none !important;
+}
+
+.note code {
+ background: #d6d6d6 !important;
+}
+
+/* ================================================================== */
+/* Syntax highlight like Python doc.
+/* ================================================================== */
+
+/* Comment */
+.highlight .err {
+ border: 1px solid #FF0000
+}
+
+/* Error */
+.highlight .k {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Keyword */
+.highlight .o {
+ color: #666666
+}
+
+/* Operator */
+.highlight .ch {
+ color: #408090;
+ font-style: italic
+}
+
+/* Comment.Hashbang */
+.highlight .cm {
+ color: #408090;
+ font-style: italic
+}
+
+/* Comment.Multiline */
+.highlight .cp {
+ color: #007020
+}
+
+/* Comment.Preproc */
+.highlight .cpf {
+ color: #408090;
+ font-style: italic
+}
+
+/* Comment.PreprocFile */
+.highlight .c1 {
+ color: #408090;
+ font-style: italic
+}
+
+/* Comment.Single */
+.highlight .cs {
+ color: #408090;
+ background-color: #fff0f0
+}
+
+/* Comment.Special */
+.highlight .gd {
+ color: #A00000
+}
+
+/* Generic.Deleted */
+.highlight .ge {
+ font-style: italic
+}
+
+/* Generic.Emph */
+.highlight .gr {
+ color: #FF0000
+}
+
+/* Generic.Error */
+.highlight .gh {
+ color: #000080;
+ font-weight: bold
+}
+
+/* Generic.Heading */
+.highlight .gi {
+ color: #00A000
+}
+
+/* Generic.Inserted */
+.highlight .go {
+ color: #333333
+}
+
+/* Generic.Output */
+.highlight .gp {
+ color: #c65d09;
+ font-weight: bold
+}
+
+/* Generic.Prompt */
+.highlight .gs {
+ font-weight: bold
+}
+
+/* Generic.Strong */
+.highlight .gu {
+ color: #800080;
+ font-weight: bold
+}
+
+/* Generic.Subheading */
+.highlight .gt {
+ color: #0044DD
+}
+
+/* Generic.Traceback */
+.highlight .kc {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Keyword.Constant */
+.highlight .kd {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Keyword.Declaration */
+.highlight .kn {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Keyword.Namespace */
+.highlight .kp {
+ color: #007020
+}
+
+/* Keyword.Pseudo */
+.highlight .kr {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Keyword.Reserved */
+.highlight .kt {
+ color: #902000
+}
+
+/* Keyword.Type */
+.highlight .m {
+ color: #208050
+}
+
+/* Literal.Number */
+.highlight .s {
+ color: #4070a0
+}
+
+/* Literal.String */
+.highlight .na {
+ color: #4070a0
+}
+
+/* Name.Attribute */
+.highlight .nb {
+ color: #007020
+}
+
+/* Name.Builtin */
+.highlight .nc {
+ color: #0e84b5;
+ font-weight: bold
+}
+
+/* Name.Class */
+.highlight .no {
+ color: #60add5
+}
+
+/* Name.Constant */
+.highlight .nd {
+ color: #555555;
+ font-weight: bold
+}
+
+/* Name.Decorator */
+.highlight .ni {
+ color: #d55537;
+ font-weight: bold
+}
+
+/* Name.Entity */
+.highlight .ne {
+ color: #007020
+}
+
+/* Name.Exception */
+.highlight .nf {
+ color: #06287e
+}
+
+/* Name.Function */
+.highlight .nl {
+ color: #002070;
+ font-weight: bold
+}
+
+/* Name.Label */
+.highlight .nn {
+ color: #0e84b5;
+ font-weight: bold
+}
+
+/* Name.Namespace */
+.highlight .nt {
+ color: #062873;
+ font-weight: bold
+}
+
+/* Name.Tag */
+.highlight .nv {
+ color: #bb60d5
+}
+
+/* Name.Variable */
+.highlight .ow {
+ color: #007020;
+ font-weight: bold
+}
+
+/* Operator.Word */
+.highlight .w {
+ color: #bbbbbb
+}
+
+/* Text.Whitespace */
+.highlight .mb {
+ color: #208050
+}
+
+/* Literal.Number.Bin */
+.highlight .mf {
+ color: #208050
+}
+
+/* Literal.Number.Float */
+.highlight .mh {
+ color: #208050
+}
+
+/* Literal.Number.Hex */
+.highlight .mi {
+ color: #208050
+}
+
+/* Literal.Number.Integer */
+.highlight .mo {
+ color: #208050
+}
+
+/* Literal.Number.Oct */
+.highlight .sa {
+ color: #4070a0
+}
+
+/* Literal.String.Affix */
+.highlight .sb {
+ color: #4070a0
+}
+
+/* Literal.String.Backtick */
+.highlight .sc {
+ color: #4070a0
+}
+
+/* Literal.String.Char */
+.highlight .dl {
+ color: #4070a0
+}
+
+/* Literal.String.Delimiter */
+.highlight .sd {
+ color: #4070a0;
+ font-style: italic
+}
+
+/* Literal.String.Doc */
+.highlight .s2 {
+ color: #4070a0
+}
+
+/* Literal.String.Double */
+.highlight .se {
+ color: #4070a0;
+ font-weight: bold
+}
+
+/* Literal.String.Escape */
+.highlight .sh {
+ color: #4070a0
+}
+
+/* Literal.String.Heredoc */
+.highlight .si {
+ color: #70a0d0;
+ font-style: italic
+}
+
+/* Literal.String.Interpol */
+.highlight .sx {
+ color: #c65d09
+}
+
+/* Literal.String.Other */
+.highlight .sr {
+ color: #235388
+}
+
+/* Literal.String.Regex */
+.highlight .s1 {
+ color: #4070a0
+}
+
+/* Literal.String.Single */
+.highlight .ss {
+ color: #517918
+}
+
+/* Literal.String.Symbol */
+.highlight .bp {
+ color: #007020
+}
+
+/* Name.Builtin.Pseudo */
+.highlight .fm {
+ color: #06287e
+}
+
+/* Name.Function.Magic */
+.highlight .vc {
+ color: #bb60d5
+}
+
+/* Name.Variable.Class */
+.highlight .vg {
+ color: #bb60d5
+}
+
+/* Name.Variable.Global */
+.highlight .vi {
+ color: #bb60d5
+}
+
+/* Name.Variable.Instance */
+.highlight .vm {
+ color: #bb60d5
+}
+
+/* Name.Variable.Magic */
+.highlight .il {
+ color: #208050
+}
diff --git a/docs/_template/globaltoc.html b/docs/_template/globaltoc.html
deleted file mode 100644
index f5fbb406..00000000
--- a/docs/_template/globaltoc.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{#
- basic/globaltoc.html
- ~~~~~~~~~~~~~~~~~~~~
-
- Sphinx sidebar template: global table of contents.
-
- :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-#}
-<h3>{{ _('Manual') }}</h3>
-{{ toctree() }}
-<a href="{{ pathto(master_doc) }}">Back to Welcome</a>
diff --git a/docs/_template/indexcontent.html b/docs/_template/indexcontent.html
deleted file mode 100644
index dd5e7249..00000000
--- a/docs/_template/indexcontent.html
+++ /dev/null
@@ -1,4 +0,0 @@
-{% extends "defindex.html" %}
-{% block tables %}
-
-{% endblock %}
diff --git a/docs/_template/indexsidebar.html b/docs/_template/indexsidebar.html
deleted file mode 100644
index 903675d1..00000000
--- a/docs/_template/indexsidebar.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<h3>Useful links</h3>
-<ul>
- <li><a href="https://github.com/giampaolo/psutil">Github project</a></li>
- <li><a href="http://grodola.blogspot.com/search/label/psutil">Blog</a></li>
- <li><a href="https://pypi.python.org/pypi?:action=display&name=psutil#downloads">Download</a></li>
- <li><a href="https://github.com/giampaolo/psutil/issues">Issues</a></li>
- <li><a href="http://groups.google.com/group/psutil/topics">Forum</a></li>
-</ul>
diff --git a/docs/_template/page.html b/docs/_template/page.html
deleted file mode 100644
index 04b47b41..00000000
--- a/docs/_template/page.html
+++ /dev/null
@@ -1,66 +0,0 @@
-{% extends "!page.html" %}
-{% block extrahead %}
-{{ super() }}
-{% if not embedded %}<script type="text/javascript" src="{{ pathto('_static/copybutton.js', 1) }}"></script>{% endif %}
-<script type="text/javascript">
-
- // Store editor pop-up help state in localStorage
- // so it does not re-pop-up itself between page loads.
- // Do not even to pretend to support IE gracefully.
- (function($) {
-
- $(document).ready(function() {
- var box = $("#editor-trap");
- var klass = "toggled";
- var storageKey = "toggled";
-
- function toggle() {
- box.toggleClass(klass);
- // Store the toggle status in local storage as "has value string" or null
- window.localStorage.setItem(storageKey, box.hasClass(klass) ? "toggled" : "not-toggled");
- }
-
- box.click(toggle);
-
- // Check the persistent state of the editor pop-up
- // Note that localStorage does not necessarily support boolean values (ugh!)
- // http://stackoverflow.com/questions/3263161/cannot-set-boolean-values-in-localstorage
- var v = window.localStorage.getItem(storageKey);
- if(v == "toggled" || !v) {
- box.addClass(klass);
- }
-
- });
-
- })(jQuery);
-</script>
-<script type="text/javascript">
-
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', 'UA-2097050-4']);
- _gaq.push(['_trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
- })();
-
-</script>
-{% endblock %}
-
-{% block rootrellink %}
- <li><a href="https://github.com/giampaolo/psutil/"><img src="{{ pathto('_static/logo.png', 1) }}" style="height: 30px; vertical-align: middle; padding-right: 1em;" /> Project Homepage</a>{{ reldelim1 }}</li>
- <li><a href="{{ pathto('index') }}">{{ shorttitle }}</a>{{ reldelim1 }}</li>
-{% endblock %}
-
-
-{% block footer %}
-<div class="footer">
- &copy; Copyright {{ copyright|e }}.
- <br />
- Last updated on {{ last_updated|e }}.
- <br />
- Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version|e }}.
-</div>
-{% endblock %} \ No newline at end of file
diff --git a/docs/_themes/pydoctheme/static/pydoctheme.css b/docs/_themes/pydoctheme/static/pydoctheme.css
deleted file mode 100644
index c6f19ab7..00000000
--- a/docs/_themes/pydoctheme/static/pydoctheme.css
+++ /dev/null
@@ -1,197 +0,0 @@
-@import url("default.css");
-
-body {
- background-color: white;
- margin-left: 1em;
- margin-right: 1em;
-}
-
-div.related {
- margin-bottom: 1.2em;
- padding: 0.5em 0;
- border-top: 1px solid #ccc;
- margin-top: 0.5em;
-}
-
-div.related a:hover {
- color: #0095C4;
-}
-
-div.related:first-child {
- border-top: 0;
- padding-top: 0;
- border-bottom: 1px solid #ccc;
-}
-
-div.sphinxsidebar {
- background-color: #eeeeee;
- border-radius: 5px;
- line-height: 130%;
- font-size: smaller;
-}
-
-div.sphinxsidebar h3, div.sphinxsidebar h4 {
- margin-top: 1.5em;
-}
-
-div.sphinxsidebarwrapper > h3:first-child {
- margin-top: 0.2em;
-}
-
-div.sphinxsidebarwrapper > ul > li > ul > li {
- margin-bottom: 0.4em;
-}
-
-div.sphinxsidebar a:hover {
- color: #0095C4;
-}
-
-div.sphinxsidebar input {
- font-family: 'Lucida Grande','Lucida Sans','DejaVu Sans',Arial,sans-serif;
- border: 1px solid #999999;
- font-size: smaller;
- border-radius: 3px;
-}
-
-div.sphinxsidebar input[type=text] {
- max-width: 150px;
-}
-
-div.body {
- padding: 0 0 0 1.2em;
-}
-
-div.body p {
- line-height: 140%;
-}
-
-div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 {
- margin: 0;
- border: 0;
- padding: 0.3em 0;
-}
-
-div.body hr {
- border: 0;
- background-color: #ccc;
- height: 1px;
-}
-
-div.body pre {
- border-radius: 3px;
- border: 1px solid #ac9;
-}
-
-div.body div.admonition, div.body div.impl-detail {
- border-radius: 3px;
-}
-
-div.body div.impl-detail > p {
- margin: 0;
-}
-
-div.body div.seealso {
- border: 1px solid #dddd66;
-}
-
-div.body a {
- color: #00608f;
-}
-
-div.body a:visited {
- color: #30306f;
-}
-
-div.body a:hover {
- color: #00B0E4;
-}
-
-tt, pre {
- font-family: monospace, sans-serif;
- font-size: 96.5%;
-}
-
-div.body tt {
- border-radius: 3px;
-}
-
-div.body tt.descname {
- font-size: 120%;
-}
-
-div.body tt.xref, div.body a tt {
- font-weight: normal;
-}
-
-p.deprecated {
- border-radius: 3px;
-}
-
-table.docutils {
- border: 1px solid #ddd;
- min-width: 20%;
- border-radius: 3px;
- margin-top: 10px;
- margin-bottom: 10px;
-}
-
-table.docutils td, table.docutils th {
- border: 1px solid #ddd !important;
- border-radius: 3px;
-}
-
-table p, table li {
- text-align: left !important;
-}
-
-table.docutils th {
- background-color: #eee;
- padding: 0.3em 0.5em;
-}
-
-table.docutils td {
- background-color: white;
- padding: 0.3em 0.5em;
-}
-
-table.footnote, table.footnote td {
- border: 0 !important;
-}
-
-div.footer {
- line-height: 150%;
- margin-top: -2em;
- text-align: right;
- width: auto;
- margin-right: 10px;
-}
-
-div.footer a:hover {
- color: #0095C4;
-}
-
-div.body h1,
-div.body h2,
-div.body h3 {
- background-color: #EAEAEA;
- border-bottom: 1px solid #CCC;
- padding-top: 2px;
- padding-bottom: 2px;
- padding-left: 5px;
- margin-top: 5px;
- margin-bottom: 5px;
-}
-
-div.body h2 {
- padding-left:10px;
-}
-
-.data {
- margin-top: 4px !important;
- margin-bottom: 4px !important;
-}
-
-.data dd {
- margin-top: 0px !important;
- margin-bottom: 0px !important;
-}
diff --git a/docs/_themes/pydoctheme/theme.conf b/docs/_themes/pydoctheme/theme.conf
deleted file mode 100644
index 95b97e53..00000000
--- a/docs/_themes/pydoctheme/theme.conf
+++ /dev/null
@@ -1,23 +0,0 @@
-[theme]
-inherit = default
-stylesheet = pydoctheme.css
-pygments_style = sphinx
-
-[options]
-bodyfont = 'Lucida Grande', 'Lucida Sans', 'DejaVu Sans', Arial, sans-serif
-headfont = 'Lucida Grande', 'Lucida Sans', 'DejaVu Sans', Arial, sans-serif
-footerbgcolor = white
-footertextcolor = #555555
-relbarbgcolor = white
-relbartextcolor = #666666
-relbarlinkcolor = #444444
-sidebarbgcolor = white
-sidebartextcolor = #444444
-sidebarlinkcolor = #444444
-bgcolor = white
-textcolor = #222222
-linkcolor = #0090c0
-visitedlinkcolor = #00608f
-headtextcolor = #1a1a1a
-headbgcolor = white
-headlinkcolor = #aaaaaa
diff --git a/docs/conf.py b/docs/conf.py
index f0a206db..df825cbd 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# psutil documentation build configuration file, created by
-# sphinx-quickstart.
+# sphinx-quickstart on Wed Oct 19 21:54:30 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
@@ -12,12 +12,22 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
import datetime
import os
PROJECT_NAME = "psutil"
-AUTHOR = "Giampaolo Rodola'"
+AUTHOR = u"Giampaolo Rodola"
THIS_YEAR = str(datetime.datetime.now().year)
HERE = os.path.abspath(os.path.dirname(__file__))
@@ -39,7 +49,8 @@ def get_version():
VERSION = get_version()
# If your documentation needs a minimal Sphinx version, state it here.
-needs_sphinx = '1.0'
+#
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@@ -51,12 +62,16 @@ extensions = ['sphinx.ext.autodoc',
'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_template']
+templates_path = ['_templates']
-# The suffix of source filenames.
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
+#
# source_encoding = 'utf-8-sig'
# The master toctree document.
@@ -65,6 +80,7 @@ master_doc = 'index'
# General information about the project.
project = PROJECT_NAME
copyright = '2009-%s, %s' % (THIS_YEAR, AUTHOR)
+author = AUTHOR
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -72,35 +88,47 @@ copyright = '2009-%s, %s' % (THIS_YEAR, AUTHOR)
#
# The short X.Y version.
version = VERSION
+# The full version, including alpha/beta/rc tags.
+release = VERSION
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
-# language = None
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
+#
# today = ''
+#
# Else, today_fmt is used as the format for a strftime call.
+#
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
+#
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-add_function_parentheses = True
+#
+# add_function_parentheses = True
+
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
+#
# add_module_names = True
-autodoc_docstring_signature = True
-
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
+#
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
@@ -109,141 +137,240 @@ pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
-# -- Options for HTML output -------------------------------------------------
+
+# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-html_theme = 'pydoctheme'
-html_theme_options = {'collapsiblesidebar': True}
+#
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ["_themes"]
+# html_theme_path = []
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-html_title = "{project} {version} documentation".format(**locals())
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = u'psutil v1.0'
# A shorter title for the navigation bar. Default is the same as html_title.
+#
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-# html_logo = 'logo.png'
+#
+# html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or
+# 32x32 pixels large.
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-html_favicon = '_static/favicon.ico'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-html_use_smartypants = True
+#
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-html_sidebars = {
- 'index': 'indexsidebar.html',
- '**': ['globaltoc.html',
- 'relations.html',
- 'sourcelink.html',
- 'searchbox.html']
-}
+#
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-# html_additional_pages = {
-# 'index': 'indexcontent.html',
-# }
+#
+# html_additional_pages = {}
# If false, no module index is generated.
-html_domain_indices = False
+#
+# html_domain_indices = True
# If false, no index is generated.
-html_use_index = True
+#
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
+#
# html_split_index = False
# If true, links to the reST sources are added to the pages.
+#
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
+#
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
# Output file base name for HTML help builder.
htmlhelp_basename = '%s-doc' % PROJECT_NAME
-# -- Options for LaTeX output ------------------------------------------------
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
-# The paper size ('letter' or 'a4').
-# latex_paper_size = 'letter'
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
-# The font size ('10pt', '11pt' or '12pt').
-# latex_font_size = '10pt'
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass
-# [howto/manual]).
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
latex_documents = [
- ('index', '%s.tex' % PROJECT_NAME,
- '%s documentation' % PROJECT_NAME, AUTHOR),
+ (master_doc, 'psutil.tex', u'psutil Documentation',
+ AUTHOR, 'manual'),
]
-# The name of an image file (relative to this directory) to place at
-# the top of the title page.
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
+#
# latex_use_parts = False
# If true, show page references after internal links.
+#
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
+#
# latex_show_urls = False
-# Additional stuff for the LaTeX preamble.
-# latex_preamble = ''
-
# Documents to append as an appendix to all manuals.
+#
# latex_appendices = []
+# It false, will not define \strong, \code, itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
# If false, no module index is generated.
+#
# latex_domain_indices = True
-# -- Options for manual page output ------------------------------------------
+# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', PROJECT_NAME, '%s documentation' % PROJECT_NAME, [AUTHOR], 1)
+ (master_doc, 'psutil', u'psutil Documentation',
+ [author], 1)
]
# If true, show URL addresses after external links.
+#
# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'psutil', u'psutil Documentation',
+ author, 'psutil', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+
+html_context = {
+ 'css_files': [
+ 'https://media.readthedocs.org/css/sphinx_rtd_theme.css',
+ 'https://media.readthedocs.org/css/readthedocs-doc-embed.css',
+ '_static/css/custom.css',
+ ],
+}
diff --git a/docs/index.rst b/docs/index.rst
index 36123d96..b35c89bf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,13 +8,13 @@ psutil documentation
Quick links
-----------
-* `Home page <https://github.com/giampaolo/psutil>`__
-* `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
-* `Blog <http://grodola.blogspot.com/search/label/psutil>`__
-* `Forum <http://groups.google.com/group/psutil/topics>`__
-* `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`__
-* `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
-* `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`__
+- `Home page <https://github.com/giampaolo/psutil>`__
+- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+- `Blog <http://grodola.blogspot.com/search/label/psutil>`__
+- `Forum <http://groups.google.com/group/psutil/topics>`__
+- `Download <https://pypi.python.org/pypi?:action=display&name=psutil#downloads>`__
+- `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
+- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`__
About
-----
@@ -25,11 +25,19 @@ retrieving information on running
in **Python**.
It is useful mainly for **system monitoring**, **profiling**, **limiting
process resources** and the **management of running processes**.
-It implements many functionalities offered by command line tools
+It implements many functionalities offered by UNIX 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
+psutil currently supports the following platforms:
+
+- **Linux**
+- **Windows**
+- **OSX**,
+- **FreeBSD, OpenBSD**, **NetBSD**
+- **Sun Solaris**
+- **AIX**
+
+...both **32-bit** and **64-bit** architectures, with Python
versions from **2.6 to 3.6** (users of Python 2.4 and 2.5 may use
`2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
`PyPy <http://pypy.org/>`__ is also known to work.
@@ -155,12 +163,14 @@ CPU
Return the number of logical CPUs in the system (same as
`os.cpu_count() <http://docs.python.org/3/library/os.html#os.cpu_count>`__
- in Python 3.4).
- This number is not equivalent to the number of CPUs the current process can
- use. The number of usable CPUs can be obtained with
+ in Python 3.4) or ``None`` if undetermined.
+ This number may not be equivalent to the number of CPUs the current process
+ can actually use in case process CPU affinity has been changed or Linux
+ cgroups are being used.
+ The number of usable CPUs can be obtained with
``len(psutil.Process().cpu_affinity())``.
If *logical* is ``False`` return the number of physical cores only (hyper
- thread CPUs are excluded). Return ``None`` if undetermined.
+ thread CPUs are excluded).
On OpenBSD and NetBSD ``psutil.cpu_count(logical=False)`` always return
``None``. Example on a system having 2 physical hyper-thread CPU cores:
@@ -170,6 +180,11 @@ CPU
>>> psutil.cpu_count(logical=False)
2
+ Example returning the number of CPUs usable by the current process:
+
+ >>> len(psutil.Process().cpu_affinity())
+ 1
+
.. function:: cpu_stats()
Return various CPU statistics as a named tuple:
@@ -404,6 +419,8 @@ Disks
numbers will always be increasing or remain the same, but never decrease.
``disk_io_counters.cache_clear()`` can be used to invalidate the *nowrap*
cache.
+ On Windows it may be ncessary to issue ``diskperf -y`` command from cmd.exe
+ first in order to enable IO counters.
>>> import psutil
>>> psutil.disk_io_counters()
@@ -538,7 +555,7 @@ Network
| ``"all"`` | the sum of all the possible families and protocols |
+----------------+-----------------------------------------------------+
- On OSX this function requires root privileges.
+ On OSX and AIX this function requires root privileges.
To get per-process connections use :meth:`Process.connections`.
Also, see
`netstat.py sample script <https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py>`__.
@@ -553,8 +570,8 @@ Network
...]
.. note::
- (OSX) :class:`psutil.AccessDenied` is always raised unless running as root.
- This is a limitation of the OS and ``lsof`` does the same.
+ (OSX and AIX) :class:`psutil.AccessDenied` is always raised unless running
+ as root. This is a limitation of the OS and ``lsof`` does the same.
.. note::
(Solaris) UNIX sockets are not supported.
@@ -687,7 +704,7 @@ Sensors
.. warning::
- This API is experimental. Backward incompatible changes may occur if
+ this API is experimental. Backward incompatible changes may occur if
deemed necessary.
.. function:: sensors_fans()
@@ -711,7 +728,7 @@ Sensors
.. warning::
- This API is experimental. Backward incompatible changes may occur if
+ this API is experimental. Backward incompatible changes may occur if
deemed necessary.
.. function:: sensors_battery()
@@ -751,9 +768,11 @@ Sensors
.. versionadded:: 5.1.0
+ .. versionchanged:: 5.4.2 added OSX support
+
.. warning::
- This API is experimental. Backward incompatible changes may occur if
+ this API is experimental. Backward incompatible changes may occur if
deemed necessary.
Other system info
@@ -830,9 +849,9 @@ Functions
Sorting order in which processes are returned is
based on their PID.
*attrs* and *ad_value* have the same meaning as in :meth:`Process.as_dict()`.
- If *attrs* is specified :meth:`Process.as_dict()` is called and the resulting
- dict is stored as a ``info`` attribute which is attached to the returned
- :class:`Process` instance.
+ If *attrs* is specified :meth:`Process.as_dict()` is called interanally and
+ the resulting dict is stored as a ``info`` attribute which is attached to the
+ returned :class:`Process` instances.
If *attrs* is an empty list it will retrieve all process info (slow).
Example usage::
@@ -900,8 +919,8 @@ Functions
``callback`` is a function which gets called when one of the processes being
waited on is terminated and a :class:`Process` instance is passed as callback
argument).
- This tunction will return as soon as all processes terminate or when
- *timeout* occurs, if specified.
+ This function will return as soon as all processes terminate or when
+ *timeout* (seconds) occurs.
Differently from :meth:`Process.wait` it will not raise
:class:`TimeoutExpired` if timeout occurs.
A typical use case may be:
@@ -986,14 +1005,13 @@ Process class
at the same time, make sure to use either :meth:`as_dict` or
:meth:`oneshot` context manager.
- .. warning::
+ .. note::
- the way this class is bound to a process is via its **PID**.
- That means that if the :class:`Process` instance is old enough and
- the PID has been reused in the meantime you might end up interacting
- with another process.
+ the way this class is bound to a process is uniquely via its **PID**.
+ That means that if the process terminates and the OS reuses its PID you may
+ end up interacting with another process.
The only exceptions for which process identity is preemptively checked
- (via PID + creation time) and guaranteed are for
+ (via PID + creation time) is for the following methods:
:meth:`nice` (set),
:meth:`ionice` (set),
:meth:`cpu_affinity` (set),
@@ -1003,12 +1021,13 @@ Process class
:meth:`suspend`
:meth:`resume`,
:meth:`send_signal`,
- :meth:`terminate`, and
- :meth:`kill`
- methods.
+ :meth:`terminate`
+ :meth:`kill`.
To prevent this problem for all other methods you can use
- :meth:`is_running()` before querying the process or use
+ :meth:`is_running()` before querying the process or
:func:`process_iter()` in case you're iterating over all processes.
+ It must be noted though that unless you deal with very "old" (inactive)
+ :class:`Process` instances this will hardly represent a problem.
.. method:: oneshot()
@@ -1046,45 +1065,45 @@ Process class
The last column (speedup) shows an approximation of the speedup you can get
if you call all the methods together (best case scenario).
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | Linux | Windows | OSX | BSD | SunOS |
- +==============================+===============================+==============================+==============================+==========================+
- | :meth:`cpu_num` | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_percent` | :meth:`cpu_num` | :meth:`name` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`~Process.cpu_percent` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_times` | :meth:`~Process.cpu_percent` | :meth:`cmdline` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`~Process.cpu_times` | :meth:`io_counters()` | :meth:`memory_info` | :meth:`~Process.cpu_times` | :meth:`create_time` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`create_time` | :meth:`ionice` | :meth:`memory_percent` | :meth:`create_time` | |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`name` | :meth:`memory_info` | :meth:`num_ctx_switches` | :meth:`gids` | :meth:`memory_info` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`ppid` | :meth:`nice` | :meth:`num_threads` | :meth:`io_counters` | :meth:`memory_percent` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`status` | :meth:`memory_maps` | | :meth:`name` | :meth:`nice` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`terminal` | :meth:`num_ctx_switches` | :meth:`create_time` | :meth:`memory_info` | :meth:`num_threads` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | | :meth:`num_handles` | :meth:`gids` | :meth:`memory_percent` | :meth:`ppid` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`gids` | :meth:`num_threads` | :meth:`name` | :meth:`num_ctx_switches` | :meth:`status` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`num_ctx_switches` | :meth:`username` | :meth:`ppid` | :meth:`ppid` | :meth:`terminal` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`num_threads` | | :meth:`status` | :meth:`status` | |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`uids` | | :meth:`terminal` | :meth:`terminal` | :meth:`gids` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`username` | | :meth:`uids` | :meth:`uids` | :meth:`uids` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | | | :meth:`username` | :meth:`username` | :meth:`username` |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`memory_full_info` | | | | |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | :meth:`memory_maps` | | | | |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
- | *speedup: +2.6x* | *speedup: +1.8x / +6.5x* | *speedup: +1.9x* | *speedup: +2.0x* | *speedup: +1.3x* |
- +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | Linux | Windows | OSX | BSD | SunOS | AIX |
+ +==============================+===============================+==============================+==============================+==========================+==========================+
+ | :meth:`cpu_num` | :meth:`cpu_percent` | :meth:`cpu_percent` | :meth:`cpu_num` | :meth:`name` | :meth:`name` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`cpu_percent` | :meth:`cpu_times` | :meth:`cpu_times` | :meth:`cpu_percent` | :meth:`cmdline` | :meth:`cmdline` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`cpu_times` | :meth:`io_counters()` | :meth:`memory_info` | :meth:`cpu_times` | :meth:`create_time` | :meth:`create_time` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`create_time` | :meth:`memory_info` | :meth:`memory_percent` | :meth:`create_time` | | |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`name` | :meth:`memory_maps` | :meth:`num_ctx_switches` | :meth:`gids` | :meth:`memory_info` | :meth:`memory_info` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`ppid` | :meth:`num_ctx_switches` | :meth:`num_threads` | :meth:`io_counters` | :meth:`memory_percent` | :meth:`memory_percent` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`status` | :meth:`num_handles` | | :meth:`name` | :meth:`num_threads` | :meth:`num_threads` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`terminal` | :meth:`num_threads` | :meth:`create_time` | :meth:`memory_info` | :meth:`ppid` | :meth:`ppid` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | | :meth:`username` | :meth:`gids` | :meth:`memory_percent` | :meth:`status` | :meth:`status` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`gids` | | :meth:`name` | :meth:`num_ctx_switches` | :meth:`terminal` | :meth:`terminal` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`num_ctx_switches` | | :meth:`ppid` | :meth:`ppid` | | |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`num_threads` | | :meth:`status` | :meth:`status` | :meth:`gids` | :meth:`gids` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`uids` | | :meth:`terminal` | :meth:`terminal` | :meth:`uids` | :meth:`uids` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`username` | | :meth:`uids` | :meth:`uids` | :meth:`username` | :meth:`username` |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | | | :meth:`username` | :meth:`username` | | |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`memory_full_info` | | | | | |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | :meth:`memory_maps` | | | | | |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
+ | *speedup: +2.6x* | *speedup: +1.8x / +6.5x* | *speedup: +1.9x* | *speedup: +2.0x* | *speedup: +1.3x* | *speedup: +1.3x* |
+ +------------------------------+-------------------------------+------------------------------+------------------------------+--------------------------+--------------------------+
.. versionadded:: 5.0.0
@@ -1138,7 +1157,7 @@ Process class
Availability: Linux, OSX, Windows, SunOS
.. versionadded:: 4.0.0
- .. versionchanged:: 5.3.0: added SunOS support
+ .. versionchanged:: 5.3.0 added SunOS support
.. method:: create_time()
@@ -1351,7 +1370,7 @@ Process class
>>> p.io_counters()
pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0, read_chars=769931, write_chars=203)
- Availability: all platforms except OSX and Solaris
+ Availability: Linux, BSD, Windows, AIX
.. versionchanged:: 5.2.0 added *read_chars* and *write_chars* on Linux;
added *other_count* and *other_bytes* on Windows.
@@ -1361,6 +1380,8 @@ Process class
The number voluntary and involuntary context switches performed by
this process (cumulative).
+ .. versionchanged:: 5.4.1 added AIX support
+
.. method:: num_fds()
The number of file descriptors currently opened by this process
@@ -1496,33 +1517,33 @@ Process class
The "portable" fields available on all plaforms are `rss` and `vms`.
All numbers are expressed in bytes.
- +---------+---------+-------+---------+------------------------------+
- | Linux | OSX | BSD | Solaris | Windows |
- +=========+=========+=======+=========+==============================+
- | rss | rss | rss | rss | rss (alias for ``wset``) |
- +---------+---------+-------+---------+------------------------------+
- | vms | vms | vms | vms | vms (alias for ``pagefile``) |
- +---------+---------+-------+---------+------------------------------+
- | shared | pfaults | text | | num_page_faults |
- +---------+---------+-------+---------+------------------------------+
- | text | pageins | data | | peak_wset |
- +---------+---------+-------+---------+------------------------------+
- | lib | | stack | | wset |
- +---------+---------+-------+---------+------------------------------+
- | data | | | | peak_paged_pool |
- +---------+---------+-------+---------+------------------------------+
- | dirty | | | | paged_pool |
- +---------+---------+-------+---------+------------------------------+
- | | | | | peak_nonpaged_pool |
- +---------+---------+-------+---------+------------------------------+
- | | | | | nonpaged_pool |
- +---------+---------+-------+---------+------------------------------+
- | | | | | pagefile |
- +---------+---------+-------+---------+------------------------------+
- | | | | | peak_pagefile |
- +---------+---------+-------+---------+------------------------------+
- | | | | | private |
- +---------+---------+-------+---------+------------------------------+
+ +---------+---------+-------+---------+-----+------------------------------+
+ | Linux | OSX | BSD | Solaris | AIX | Windows |
+ +=========+=========+=======+=========+=====+==============================+
+ | rss | rss | rss | rss | rss | rss (alias for ``wset``) |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | vms | vms | vms | vms | vms | vms (alias for ``pagefile``) |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | shared | pfaults | text | | | num_page_faults |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | text | pageins | data | | | peak_wset |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | lib | | stack | | | wset |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | data | | | | | peak_paged_pool |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | dirty | | | | | paged_pool |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | | | | | | peak_nonpaged_pool |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | | | | | | nonpaged_pool |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | | | | | | pagefile |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | | | | | | peak_pagefile |
+ +---------+---------+-------+---------+-----+------------------------------+
+ | | | | | | private |
+ +---------+---------+-------+---------+-----+------------------------------+
- **rss**: aka "Resident Set Size", this is the non-swapped physical
memory a process has used.
@@ -1693,13 +1714,13 @@ Process class
pmmap_ext(addr='02829000-02ccf000', perms='rw-p', path='[heap]', rss=4743168, size=4874240, pss=4743168, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=4743168, referenced=4718592, anonymous=4743168, swap=0),
...]
- Availability: All platforms except OpenBSD and NetBSD.
+ Availability: All platforms except OpenBSD, NetBSD and AIX.
.. method:: children(recursive=False)
- Return the children of this process as a list of :Class:`Process` objects,
- preemptively checking whether PID has been reused. If recursive is `True`
- return all the parent descendants.
+ Return the children of this process as a list of :class:`Process`
+ instances.
+ If recursive is `True` return all the parent descendants.
Pseudo code example assuming *A == this process*:
::
@@ -1719,7 +1740,7 @@ Process class
Note that in the example above if process X disappears process Y won't be
returned either as the reference to process A is lost.
This concept is well summaried by this
- `unit test <https://github.com/giampaolo/psutil/blob/fb9ae861cf3cf175c3da4a3cd4e558c6cbd6af91/psutil/tests/test_process.py#L1236-L1247>`__.
+ `unit test <https://github.com/giampaolo/psutil/blob/65a52341b55faaab41f68ebc4ed31f18f0929754/psutil/tests/test_process.py#L1064-L1075>`__.
See also how to `kill a process tree <#kill-process-tree>`__ and
`terminate my children <#terminate-my-children>`__.
@@ -1855,6 +1876,10 @@ Process class
(OpenBSD) "laddr" and "raddr" fields for UNIX sockets are always set to
"". This is a limitation of the OS.
+ .. note::
+ (AIX) :class:`psutil.AccessDenied` is always raised unless running
+ as root (lsof does the same).
+
.. versionchanged:: 5.3.0 : "laddr" and "raddr" are named tuples.
.. method:: is64bit()
@@ -1928,14 +1953,15 @@ Process class
.. method:: wait(timeout=None)
- Wait for process termination and if the process is a children of the
- current one also return the exit code, else ``None``. On Windows there's
+ Wait for process termination and if the process is a child of the current
+ one also return the exit code, else ``None``. On Windows there's
no such limitation (exit code is always returned). If the process is
already terminated immediately return ``None`` instead of raising
- :class:`NoSuchProcess`. If *timeout* is specified and process is still
- alive raise :class:`TimeoutExpired` exception. It can also be used in a
- non-blocking fashion by specifying ``timeout=0`` in which case it will
- either return immediately or raise :class:`TimeoutExpired`.
+ :class:`NoSuchProcess`.
+ *timeout* is expressed in seconds. If specified and the process is still
+ alive raise :class:`TimeoutExpired` exception.
+ ``timeout=0`` can be used in non-blocking apps: it will either return
+ immediately or raise :class:`TimeoutExpired`.
To wait for multiple processes use :func:`psutil.wait_procs()`.
>>> import psutil
@@ -2104,16 +2130,18 @@ Constants
.. data:: OPENBSD
.. data:: BSD
.. data:: SUNOS
+.. data:: AIX
``bool`` constants which define what platform you're on. E.g. if on Windows,
:const:`WINDOWS` constant will be ``True``, all others will be ``False``.
.. versionadded:: 4.0.0
+ .. versionchanged:: 5.4.0 added AIX
.. _const-procfs_path:
.. data:: PROCFS_PATH
- The path of the /proc filesystem on Linux and Solaris (defaults to
+ The path of the /proc filesystem on Linux, Solaris and AIX (defaults to
``"/proc"``).
You may want to re-set this constant right after importing psutil in case
your /proc filesystem is mounted elsewhere or if you want to retrieve
@@ -2126,10 +2154,11 @@ Constants
It must be noted that this trick works only for APIs which rely on /proc
filesystem (e.g. `memory`_ APIs and most :class:`Process` class methods).
- Availability: Linux, Solaris
+ Availability: Linux, Solaris, AIX
.. versionadded:: 3.2.3
.. versionchanged:: 3.4.2 also available on Solaris.
+ .. versionchanged:: 5.4.0 also available on AIX.
.. _const-pstatus:
.. data:: STATUS_RUNNING
@@ -2415,13 +2444,13 @@ resources.
for p in procs:
p.terminate()
gone, alive = psutil.wait_procs(procs, timeout=timeout, callback=on_terminate)
- if not alive:
+ if alive:
# send SIGKILL
for p in alive:
print("process {} survived SIGTERM; trying SIGKILL" % p)
p.kill()
gone, alive = psutil.wait_procs(alive, timeout=timeout, callback=on_terminate)
- if not alive:
+ if alive:
# give up
for p in alive:
print("process {} survived SIGKILL; giving up" % p)
@@ -2506,8 +2535,8 @@ Top 3 processes opening more file descriptors::
(2721, {'name': 'chrome', 'num_fds': 185}),
(2650, {'name': 'chrome', 'num_fds': 354})]
-Q&A
-===
+FAQs
+====
* Q: What Windows versions are supported?
* A: From Windows **Vista** onwards, both 32 and 64 bit versions.
@@ -2520,6 +2549,13 @@ Q&A
----
+* Q: What Python versions are supported?
+* A: From 2.6 to 3.6, both 32 and 64 bit versions. Last version supporting
+ Python 2.4 and 2.5 is `psutil 2.1.3 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__.
+ PyPy is also known to work.
+
+----
+
* Q: What SunOS versions are supported?
* A: From Solaris 10 onwards.
@@ -2542,7 +2578,7 @@ Q&A
* Q: What about load average?
* A: psutil does not expose any load average function as it's already available
in python as
- `os.getloadavg <https://docs.python.org/2/library/os.html#os.getloadavg>`__
+ `os.getloadavg <https://docs.python.org/2/library/os.html#os.getloadavg>`__.
Running tests
=============
@@ -2566,6 +2602,30 @@ take a look at the
Timeline
========
+- 2018-01-01:
+ `5.4.3 <https://pypi.python.org/pypi?name=psutil&version=5.4.3&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#542>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.4.2...release-5.4.3#files_bucket>`__
+- 2017-12-07:
+ `5.4.2 <https://pypi.python.org/pypi?name=psutil&version=5.4.2&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#542>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.4.1...release-5.4.2#files_bucket>`__
+- 2017-11-08:
+ `5.4.1 <https://pypi.python.org/pypi?name=psutil&version=5.4.1&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#541>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.4.0...release-5.4.1#files_bucket>`__
+- 2017-10-12:
+ `5.4.0 <https://pypi.python.org/pypi?name=psutil&version=5.4.0&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#540>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.3.1...release-5.4.0#files_bucket>`__
+- 2017-09-10:
+ `5.3.1 <https://pypi.python.org/pypi?name=psutil&version=5.3.1&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#530>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.3.0...release-5.3.1#files_bucket>`__
+- 2017-09-01:
+ `5.3.0 <https://pypi.python.org/pypi?name=psutil&version=5.3.0&:action=files>`__ -
+ `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#530>`__ -
+ `diff <https://github.com/giampaolo/psutil/compare/release-5.2.2...release-5.3.0#files_bucket>`__
- 2017-04-10:
`5.2.2 <https://pypi.python.org/pypi?name=psutil&version=5.2.2&:action=files>`__ -
`what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#522>`__ -
diff --git a/psutil/DEVNOTES b/psutil/DEVNOTES
new file mode 100644
index 00000000..4fd15ea3
--- /dev/null
+++ b/psutil/DEVNOTES
@@ -0,0 +1,5 @@
+API REFERENCES
+==============
+
+- psutil.sensors_battery:
+ https://github.com/Kentzo/Power/
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 9c664a0c..8f929846 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -11,10 +11,11 @@ sensors) in Python. Supported platforms:
- Linux
- Windows
- OSX
- - Sun Solaris
- FreeBSD
- OpenBSD
- NetBSD
+ - Sun Solaris
+ - AIX
Works with Python versions from 2.6 to 3.X.
"""
@@ -23,6 +24,7 @@ from __future__ import division
import collections
import contextlib
+import datetime
import errno
import functools
import os
@@ -73,6 +75,7 @@ from ._common import NIC_DUPLEX_FULL
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import AIX
from ._common import BSD
from ._common import FREEBSD # NOQA
from ._common import LINUX
@@ -83,6 +86,12 @@ from ._common import POSIX # NOQA
from ._common import SUNOS
from ._common import WINDOWS
+from ._exceptions import AccessDenied
+from ._exceptions import Error
+from ._exceptions import NoSuchProcess
+from ._exceptions import TimeoutExpired
+from ._exceptions import ZombieProcess
+
if LINUX:
# This is public API and it will be retrieved from _pslinux.py
# via sys.modules.
@@ -158,6 +167,13 @@ elif SUNOS:
# _pssunos.py via sys.modules.
PROCFS_PATH = "/proc"
+elif AIX:
+ from . import _psaix as _psplatform
+
+ # This is public API and it will be retrieved from _pslinux.py
+ # via sys.modules.
+ PROCFS_PATH = "/proc"
+
else: # pragma: no cover
raise NotImplementedError('platform %s is not supported' % sys.platform)
@@ -185,7 +201,7 @@ __all__ = [
"POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
"BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS",
- "WINDOWS",
+ "WINDOWS", "AIX",
# classes
"Process", "Popen",
@@ -203,7 +219,7 @@ __all__ = [
]
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "5.3.0"
+__version__ = "5.4.3"
version_info = tuple([int(num) for num in __version__.split('.')])
AF_LINK = _psplatform.AF_LINK
POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
@@ -235,112 +251,29 @@ if (int(__version__.replace('.', '')) !=
# =====================================================================
-# --- exceptions
+# --- Utils
# =====================================================================
-class Error(Exception):
- """Base exception class. All other psutil exceptions inherit
- from this one.
- """
-
- def __init__(self, msg=""):
- Exception.__init__(self, msg)
- self.msg = msg
-
- def __repr__(self):
- ret = "%s.%s %s" % (self.__class__.__module__,
- self.__class__.__name__, self.msg)
- return ret.strip()
-
- __str__ = __repr__
-
-
-class NoSuchProcess(Error):
- """Exception raised when a process with a certain PID doesn't
- or no longer exists.
- """
-
- def __init__(self, pid, name=None, msg=None):
- Error.__init__(self, msg)
- self.pid = pid
- self.name = name
- self.msg = msg
- if msg is None:
- if name:
- details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
- else:
- details = "(pid=%s)" % self.pid
- self.msg = "process no longer exists " + details
-
-
-class ZombieProcess(NoSuchProcess):
- """Exception raised when querying a zombie process. This is
- raised on OSX, BSD and Solaris only, and not always: depending
- on the query the OS may be able to succeed anyway.
- On Linux all zombie processes are querable (hence this is never
- raised). Windows doesn't have zombie processes.
- """
-
- def __init__(self, pid, name=None, ppid=None, msg=None):
- NoSuchProcess.__init__(self, msg)
- self.pid = pid
- self.ppid = ppid
- self.name = name
- self.msg = msg
- if msg is None:
- args = ["pid=%s" % pid]
- if name:
- args.append("name=%s" % repr(self.name))
- if ppid:
- args.append("ppid=%s" % self.ppid)
- details = "(%s)" % ", ".join(args)
- self.msg = "process still exists but it's a zombie " + details
-
-
-class AccessDenied(Error):
- """Exception raised when permission to perform an action is denied."""
-
- def __init__(self, pid=None, name=None, msg=None):
- Error.__init__(self, msg)
- self.pid = pid
- self.name = name
- self.msg = msg
- if msg is None:
- if (pid is not None) and (name is not None):
- self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
- elif (pid is not None):
- self.msg = "(pid=%s)" % self.pid
+if hasattr(_psplatform, 'ppid_map'):
+ # Faster version (Windows and Linux).
+ _ppid_map = _psplatform.ppid_map
+else:
+ def _ppid_map():
+ """Return a {pid: ppid, ...} dict for all running processes in
+ one shot. Used to speed up Process.children().
+ """
+ ret = {}
+ for pid in pids():
+ try:
+ proc = _psplatform.Process(pid)
+ ppid = proc.ppid()
+ except (NoSuchProcess, AccessDenied):
+ # Note: AccessDenied is unlikely to happen.
+ pass
else:
- self.msg = ""
-
-
-class TimeoutExpired(Error):
- """Raised on Process.wait(timeout) if timeout expires and process
- is still alive.
- """
-
- def __init__(self, seconds, pid=None, name=None):
- Error.__init__(self, "timeout after %s seconds" % seconds)
- self.seconds = seconds
- self.pid = pid
- self.name = name
- if (pid is not None) and (name is not None):
- self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
- elif (pid is not None):
- self.msg += " (pid=%s)" % self.pid
-
-
-# push exception classes into platform specific module namespace
-_psplatform.NoSuchProcess = NoSuchProcess
-_psplatform.ZombieProcess = ZombieProcess
-_psplatform.AccessDenied = AccessDenied
-_psplatform.TimeoutExpired = TimeoutExpired
-
-
-# =====================================================================
-# --- Process class
-# =====================================================================
+ ret[pid] = ppid
+ return ret
def _assert_pid_not_reused(fun):
@@ -355,6 +288,22 @@ def _assert_pid_not_reused(fun):
return wrapper
+def _pprint_secs(secs):
+ """Format seconds in a human readable form."""
+ now = time.time()
+ secs_ago = int(now - secs)
+ if secs_ago < 60 * 60 * 24:
+ fmt = "%H:%M:%S"
+ else:
+ fmt = "%Y-%m-%d %H:%M:%S"
+ return datetime.datetime.fromtimestamp(secs).strftime(fmt)
+
+
+# =====================================================================
+# --- Process class
+# =====================================================================
+
+
class Process(object):
"""Represents an OS process with the given PID.
If PID is omitted current process PID (os.getpid()) is used.
@@ -440,21 +389,26 @@ class Process(object):
def __str__(self):
try:
- pid = self.pid
- name = repr(self.name())
+ info = collections.OrderedDict()
+ except AttributeError:
+ info = {} # Python 2.6
+ info["pid"] = self.pid
+ try:
+ info["name"] = self.name()
+ if self._create_time:
+ info['started'] = _pprint_secs(self._create_time)
except ZombieProcess:
- details = "(pid=%s (zombie))" % self.pid
+ info["status"] = "zombie"
except NoSuchProcess:
- details = "(pid=%s (terminated))" % self.pid
+ info["status"] = "terminated"
except AccessDenied:
- details = "(pid=%s)" % (self.pid)
- else:
- details = "(pid=%s, name=%s)" % (pid, name)
- return "%s.%s%s" % (self.__class__.__module__,
- self.__class__.__name__, details)
+ pass
+ return "%s.%s(%s)" % (
+ self.__class__.__module__,
+ self.__class__.__name__,
+ ", ".join(["%s=%r" % (k, v) for k, v in info.items()]))
- def __repr__(self):
- return "<%s at %s>" % (self.__str__(), id(self))
+ __repr__ = __str__
def __eq__(self, other):
# Test for equality with another Process object based
@@ -785,7 +739,7 @@ class Process(object):
"""
return self._proc.num_fds()
- # Linux, BSD and Windows only
+ # Linux, BSD, AIX and Windows only
if hasattr(_psplatform.Process, "io_counters"):
def io_counters(self):
@@ -908,13 +862,15 @@ class Process(object):
"""Return the number of threads used by this process."""
return self._proc.num_threads()
- def threads(self):
- """Return threads opened by process as a list of
- (id, user_time, system_time) namedtuples representing
- thread id and thread CPU times (user/system).
- On OpenBSD this method requires root access.
- """
- return self._proc.threads()
+ if hasattr(_psplatform.Process, "threads"):
+
+ def threads(self):
+ """Return threads opened by process as a list of
+ (id, user_time, system_time) namedtuples representing
+ thread id and thread CPU times (user/system).
+ On OpenBSD this method requires root access.
+ """
+ return self._proc.threads()
@_assert_pid_not_reused
def children(self, recursive=False):
@@ -943,73 +899,47 @@ class Process(object):
process Y won't be listed as the reference to process A
is lost.
"""
- if hasattr(_psplatform, 'ppid_map'):
- # Windows only: obtain a {pid:ppid, ...} dict for all running
- # processes in one shot (faster).
- ppid_map = _psplatform.ppid_map()
- else:
- ppid_map = None
-
+ ppid_map = _ppid_map()
ret = []
if not recursive:
- if ppid_map is None:
- # 'slow' version, common to all platforms except Windows
- for p in process_iter():
+ for pid, ppid in ppid_map.items():
+ if ppid == self.pid:
try:
- if p.ppid() == self.pid:
- # if child happens to be older than its parent
- # (self) it means child's PID has been reused
- if self.create_time() <= p.create_time():
- ret.append(p)
+ child = Process(pid)
+ # if child happens to be older than its parent
+ # (self) it means child's PID has been reused
+ if self.create_time() <= child.create_time():
+ ret.append(child)
except (NoSuchProcess, ZombieProcess):
pass
- else: # pragma: no cover
- # Windows only (faster)
- for pid, ppid in ppid_map.items():
- if ppid == self.pid:
- try:
- child = Process(pid)
- # if child happens to be older than its parent
- # (self) it means child's PID has been reused
- if self.create_time() <= child.create_time():
- ret.append(child)
- except (NoSuchProcess, ZombieProcess):
- pass
else:
- # construct a dict where 'values' are all the processes
- # having 'key' as their parent
- table = collections.defaultdict(list)
- if ppid_map is None:
- for p in process_iter():
- try:
- table[p.ppid()].append(p)
- except (NoSuchProcess, ZombieProcess):
- pass
- else: # pragma: no cover
- for pid, ppid in ppid_map.items():
- try:
- p = Process(pid)
- table[ppid].append(p)
- except (NoSuchProcess, ZombieProcess):
- pass
- # At this point we have a mapping table where table[self.pid]
- # are the current process' children.
- # Below, we look for all descendants recursively, similarly
- # to a recursive function call.
- checkpids = [self.pid]
- for pid in checkpids:
- for child in table[pid]:
+ # Construct a {pid: [child pids]} dict
+ reverse_ppid_map = collections.defaultdict(list)
+ for pid, ppid in ppid_map.items():
+ reverse_ppid_map[ppid].append(pid)
+ # Recursively traverse that dict, starting from self.pid,
+ # such that we only call Process() on actual children
+ seen = set()
+ stack = [self.pid]
+ while stack:
+ pid = stack.pop()
+ if pid in seen:
+ # Since pids can be reused while the ppid_map is
+ # constructed, there may be rare instances where
+ # there's a cycle in the recorded process "tree".
+ continue
+ seen.add(pid)
+ for child_pid in reverse_ppid_map[pid]:
try:
+ child = Process(child_pid)
# if child happens to be older than its parent
# (self) it means child's PID has been reused
intime = self.create_time() <= child.create_time()
- except (NoSuchProcess, ZombieProcess):
- pass
- else:
if intime:
ret.append(child)
- if child.pid not in checkpids:
- checkpids.append(child.pid)
+ stack.append(child_pid)
+ except (NoSuchProcess, ZombieProcess):
+ pass
return ret
def cpu_percent(self, interval=None):
@@ -1157,13 +1087,11 @@ class Process(object):
('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
"""
valid_types = list(_psplatform.pfullmem._fields)
- if hasattr(_psplatform, "pfullmem"):
- valid_types.extend(list(_psplatform.pfullmem._fields))
if memtype not in valid_types:
raise ValueError("invalid memtype %r; valid types are %r" % (
memtype, tuple(valid_types)))
- fun = self.memory_full_info if memtype in ('uss', 'pss', 'swap') else \
- self.memory_info
+ fun = self.memory_info if memtype in _psplatform.pmem._fields else \
+ self.memory_full_info
metrics = fun()
value = getattr(metrics, memtype)
@@ -1179,7 +1107,6 @@ class Process(object):
if hasattr(_psplatform.Process, "memory_maps"):
# Available everywhere except OpenBSD and NetBSD.
-
def memory_maps(self, grouped=True):
"""Return process' mapped memory regions as a list of namedtuples
whose fields are variable depending on the platform.
@@ -2308,7 +2235,7 @@ if hasattr(_psplatform, "sensors_fans"):
__all__.append("sensors_fans")
-# Linux, Windows, FreeBSD
+# Linux, Windows, FreeBSD, OSX
if hasattr(_psplatform, "sensors_battery"):
def sensors_battery():
@@ -2378,8 +2305,6 @@ def test(): # pragma: no cover
"""List info of all currently running processes emulating ps aux
output.
"""
- import datetime
-
today_day = datetime.date.today()
templ = "%-10s %5s %4s %7s %7s %-13s %5s %7s %s"
attrs = ['pid', 'memory_percent', 'name', 'cpu_times', 'create_time',
diff --git a/psutil/_common.py b/psutil/_common.py
index 7c4af3d8..870971e4 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -81,6 +81,7 @@ OPENBSD = sys.platform.startswith("openbsd")
NETBSD = sys.platform.startswith("netbsd")
BSD = FREEBSD or OPENBSD or NETBSD
SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris")
+AIX = sys.platform.startswith("aix")
# ===================================================================
@@ -460,14 +461,14 @@ def deprecated_method(replacement):
'replcement' is the method name which will be called instead.
"""
def outer(fun):
- msg = "%s() is deprecated; use %s() instead" % (
+ msg = "%s() is deprecated and will be removed; use %s() instead" % (
fun.__name__, replacement)
if fun.__doc__ is None:
fun.__doc__ = msg
@functools.wraps(fun)
def inner(self, *args, **kwargs):
- warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
+ warnings.warn(msg, category=FutureWarning, stacklevel=2)
return getattr(self, replacement)(*args, **kwargs)
return inner
return outer
diff --git a/psutil/_exceptions.py b/psutil/_exceptions.py
new file mode 100644
index 00000000..c08e6d83
--- /dev/null
+++ b/psutil/_exceptions.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+class Error(Exception):
+ """Base exception class. All other psutil exceptions inherit
+ from this one.
+ """
+
+ def __init__(self, msg=""):
+ Exception.__init__(self, msg)
+ self.msg = msg
+
+ def __repr__(self):
+ ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
+ return ret.strip()
+
+ __str__ = __repr__
+
+
+class NoSuchProcess(Error):
+ """Exception raised when a process with a certain PID doesn't
+ or no longer exists.
+ """
+
+ def __init__(self, pid, name=None, msg=None):
+ Error.__init__(self, msg)
+ self.pid = pid
+ self.name = name
+ self.msg = msg
+ if msg is None:
+ if name:
+ details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
+ else:
+ details = "(pid=%s)" % self.pid
+ self.msg = "process no longer exists " + details
+
+
+class ZombieProcess(NoSuchProcess):
+ """Exception raised when querying a zombie process. This is
+ raised on OSX, BSD and Solaris only, and not always: depending
+ on the query the OS may be able to succeed anyway.
+ On Linux all zombie processes are querable (hence this is never
+ raised). Windows doesn't have zombie processes.
+ """
+
+ def __init__(self, pid, name=None, ppid=None, msg=None):
+ NoSuchProcess.__init__(self, msg)
+ self.pid = pid
+ self.ppid = ppid
+ self.name = name
+ self.msg = msg
+ if msg is None:
+ args = ["pid=%s" % pid]
+ if name:
+ args.append("name=%s" % repr(self.name))
+ if ppid:
+ args.append("ppid=%s" % self.ppid)
+ details = "(%s)" % ", ".join(args)
+ self.msg = "process still exists but it's a zombie " + details
+
+
+class AccessDenied(Error):
+ """Exception raised when permission to perform an action is denied."""
+
+ def __init__(self, pid=None, name=None, msg=None):
+ Error.__init__(self, msg)
+ self.pid = pid
+ self.name = name
+ self.msg = msg
+ if msg is None:
+ if (pid is not None) and (name is not None):
+ self.msg = "(pid=%s, name=%s)" % (pid, repr(name))
+ elif (pid is not None):
+ self.msg = "(pid=%s)" % self.pid
+ else:
+ self.msg = ""
+
+
+class TimeoutExpired(Error):
+ """Raised on Process.wait(timeout) if timeout expires and process
+ is still alive.
+ """
+
+ def __init__(self, seconds, pid=None, name=None):
+ Error.__init__(self, "timeout after %s seconds" % seconds)
+ self.seconds = seconds
+ self.pid = pid
+ self.name = name
+ if (pid is not None) and (name is not None):
+ self.msg += " (pid=%s, name=%s)" % (pid, repr(name))
+ elif (pid is not None):
+ self.msg += " (pid=%s)" % self.pid
diff --git a/psutil/_psaix.py b/psutil/_psaix.py
new file mode 100644
index 00000000..9abc8d17
--- /dev/null
+++ b/psutil/_psaix.py
@@ -0,0 +1,573 @@
+# Copyright (c) 2009, Giampaolo Rodola'
+# Copyright (c) 2017, Arnon Yaari
+# All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""AIX platform implementation."""
+
+import errno
+import glob
+import os
+import re
+import subprocess
+import sys
+from collections import namedtuple
+from socket import AF_INET
+
+from . import _common
+from . import _psposix
+from . import _psutil_aix as cext
+from . import _psutil_posix as cext_posix
+from ._common import AF_INET6
+from ._common import memoize_when_activated
+from ._common import NIC_DUPLEX_FULL
+from ._common import NIC_DUPLEX_HALF
+from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import sockfam_to_enum
+from ._common import socktype_to_enum
+from ._common import usage_percent
+from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
+
+
+__extra__all__ = ["PROCFS_PATH"]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+HAS_THREADS = hasattr(cext, "proc_threads")
+
+PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = cext_posix.AF_LINK
+
+PROC_STATUSES = {
+ cext.SIDL: _common.STATUS_IDLE,
+ cext.SZOMB: _common.STATUS_ZOMBIE,
+ cext.SACTIVE: _common.STATUS_RUNNING,
+ cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this?
+ cext.SSTOP: _common.STATUS_STOPPED,
+}
+
+TCP_STATUSES = {
+ cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
+ cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
+ cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
+ cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
+ cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
+ cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
+ cext.TCPS_CLOSED: _common.CONN_CLOSE,
+ cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
+ cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
+ cext.TCPS_LISTEN: _common.CONN_LISTEN,
+ cext.TCPS_CLOSING: _common.CONN_CLOSING,
+ cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
+}
+
+proc_info_map = dict(
+ ppid=0,
+ rss=1,
+ vms=2,
+ create_time=3,
+ nice=4,
+ num_threads=5,
+ status=6,
+ ttynr=7)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms'])
+# psutil.Process.memory_full_info()
+pfullmem = pmem
+# psutil.Process.cpu_times()
+scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
+# psutil.virtual_memory()
+svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
+# psutil.Process.memory_maps(grouped=True)
+pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
+# psutil.Process.memory_maps(grouped=False)
+pmmap_ext = namedtuple(
+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+def get_procfs_path():
+ """Return updated psutil.PROCFS_PATH constant."""
+ return sys.modules['psutil'].PROCFS_PATH
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+ total, avail, free, pinned, inuse = cext.virtual_mem()
+ percent = usage_percent((total - avail), total, _round=1)
+ return svmem(total, avail, percent, inuse, free)
+
+
+def swap_memory():
+ """Swap system memory as a (total, used, free, sin, sout) tuple."""
+ total, free, sin, sout = cext.swap_mem()
+ used = total - free
+ percent = usage_percent(used, total, _round=1)
+ return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return system-wide CPU times as a named tuple"""
+ ret = cext.per_cpu_times()
+ return scputimes(*[sum(x) for x in zip(*ret)])
+
+
+def per_cpu_times():
+ """Return system per-CPU times as a list of named tuples"""
+ ret = cext.per_cpu_times()
+ return [scputimes(*x) for x in ret]
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ try:
+ return os.sysconf("SC_NPROCESSORS_ONLN")
+ except ValueError:
+ # mimic os.cpu_count() behavior
+ return None
+
+
+def cpu_count_physical():
+ cmd = "lsdev -Cc processor"
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if p.returncode != 0:
+ raise RuntimeError("%r command error\n%s" % (cmd, stderr))
+ processors = stdout.strip().splitlines()
+ return len(processors) or None
+
+
+def cpu_stats():
+ """Return various CPU stats as a named tuple."""
+ ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+disk_usage = _psposix.disk_usage
+
+
+def disk_partitions(all=False):
+ """Return system disk partitions."""
+ # TODO - the filtering logic should be better checked so that
+ # it tries to reflect 'df' as much as possible
+ retlist = []
+ partitions = cext.disk_partitions()
+ for partition in partitions:
+ device, mountpoint, fstype, opts = partition
+ if device == 'none':
+ device = ''
+ if not all:
+ # Differently from, say, Linux, we don't have a list of
+ # common fs types so the best we can do, AFAIK, is to
+ # filter by filesystem having a total size > 0.
+ if not disk_usage(mountpoint).total:
+ continue
+ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
+ retlist.append(ntuple)
+ return retlist
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_if_addrs = cext_posix.net_if_addrs
+net_io_counters = cext.net_io_counters
+
+
+def net_connections(kind, _pid=-1):
+ """Return socket connections. If pid == -1 return system-wide
+ connections (as opposed to connections opened by one process only).
+ """
+ cmap = _common.conn_tmap
+ if kind not in cmap:
+ raise ValueError("invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in cmap])))
+ families, types = _common.conn_tmap[kind]
+ rawlist = cext.net_connections(_pid)
+ ret = set()
+ for item in rawlist:
+ fd, fam, type_, laddr, raddr, status, pid = item
+ if fam not in families:
+ continue
+ if type_ not in types:
+ continue
+ status = TCP_STATUSES[status]
+ if fam in (AF_INET, AF_INET6):
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
+ fam = sockfam_to_enum(fam)
+ type_ = socktype_to_enum(type_)
+ if _pid == -1:
+ nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
+ else:
+ nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
+ ret.add(nt)
+ return list(ret)
+
+
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ duplex_map = {"Full": NIC_DUPLEX_FULL,
+ "Half": NIC_DUPLEX_HALF}
+ names = set([x[0] for x in net_if_addrs()])
+ ret = {}
+ for name in names:
+ isup, mtu = cext.net_if_stats(name)
+
+ # try to get speed and duplex
+ # TODO: rewrite this in C (entstat forks, so use truss -f to follow.
+ # looks like it is using an undocumented ioctl?)
+ duplex = ""
+ speed = 0
+ p = subprocess.Popen(["/usr/bin/entstat", "-d", name],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if p.returncode == 0:
+ re_result = re.search("Running: (\d+) Mbps.*?(\w+) Duplex", stdout)
+ if re_result is not None:
+ speed = int(re_result.group(1))
+ duplex = re_result.group(2)
+
+ duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ return ret
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+ """The system boot time expressed in seconds since the epoch."""
+ return cext.boot_time()
+
+
+def users():
+ """Return currently connected users as a list of namedtuples."""
+ retlist = []
+ rawlist = cext.users()
+ localhost = (':0.0', ':0')
+ for item in rawlist:
+ user, tty, hostname, tstamp, user_process, pid = item
+ # note: the underlying C function includes entries about
+ # system boot, run level and others. We might want
+ # to use them in the future.
+ if not user_process:
+ continue
+ if hostname in localhost:
+ hostname = 'localhost'
+ nt = _common.suser(user, tty, hostname, tstamp, pid)
+ retlist.append(nt)
+ return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+ """Returns a list of PIDs currently running on the system."""
+ return [int(x) for x in os.listdir(get_procfs_path()) if x.isdigit()]
+
+
+def pid_exists(pid):
+ """Check for the existence of a unix pid."""
+ return os.path.exists(os.path.join(get_procfs_path(), str(pid), "psinfo"))
+
+
+def wrap_exceptions(fun):
+ """Call callable into a try/except clause and translate ENOENT,
+ EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
+ """
+
+ def wrapper(self, *args, **kwargs):
+ try:
+ return fun(self, *args, **kwargs)
+ except EnvironmentError as err:
+ # support for private module import
+ if (NoSuchProcess is None or AccessDenied is None or
+ ZombieProcess is None):
+ 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.
+ if err.errno in (errno.ENOENT, errno.ESRCH):
+ if not pid_exists(self.pid):
+ raise NoSuchProcess(self.pid, self._name)
+ else:
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ if err.errno in (errno.EPERM, errno.EACCES):
+ raise AccessDenied(self.pid, self._name)
+ raise
+ return wrapper
+
+
+class Process(object):
+ """Wrapper class around underlying C implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+ self._procfs_path = get_procfs_path()
+
+ def oneshot_enter(self):
+ self._proc_name_and_args.cache_activate()
+ self._proc_basic_info.cache_activate()
+ self._proc_cred.cache_activate()
+
+ def oneshot_exit(self):
+ self._proc_name_and_args.cache_deactivate()
+ self._proc_basic_info.cache_deactivate()
+ self._proc_cred.cache_deactivate()
+
+ @memoize_when_activated
+ def _proc_name_and_args(self):
+ return cext.proc_name_and_args(self.pid, self._procfs_path)
+
+ @memoize_when_activated
+ def _proc_basic_info(self):
+ return cext.proc_basic_info(self.pid, self._procfs_path)
+
+ @memoize_when_activated
+ def _proc_cred(self):
+ return cext.proc_cred(self.pid, self._procfs_path)
+
+ @wrap_exceptions
+ def name(self):
+ if self.pid == 0:
+ return "swapper"
+ # note: this is limited to 15 characters
+ return self._proc_name_and_args()[0].rstrip("\x00")
+
+ @wrap_exceptions
+ def exe(self):
+ # there is no way to get executable path in AIX other than to guess,
+ # and guessing is more complex than what's in the wrapping class
+ exe = self.cmdline()[0]
+ if os.path.sep in exe:
+ # relative or absolute path
+ if not os.path.isabs(exe):
+ # if cwd has changed, we're out of luck - this may be wrong!
+ exe = os.path.abspath(os.path.join(self.cwd(), exe))
+ if (os.path.isabs(exe) and
+ os.path.isfile(exe) and
+ os.access(exe, os.X_OK)):
+ return exe
+ # not found, move to search in PATH using basename only
+ exe = os.path.basename(exe)
+ # search for exe name PATH
+ for path in os.environ["PATH"].split(":"):
+ possible_exe = os.path.abspath(os.path.join(path, exe))
+ if (os.path.isfile(possible_exe) and
+ os.access(possible_exe, os.X_OK)):
+ return possible_exe
+ return ''
+
+ @wrap_exceptions
+ def cmdline(self):
+ return self._proc_name_and_args()[1].split(' ')
+
+ @wrap_exceptions
+ def create_time(self):
+ return self._proc_basic_info()[proc_info_map['create_time']]
+
+ @wrap_exceptions
+ def num_threads(self):
+ return self._proc_basic_info()[proc_info_map['num_threads']]
+
+ if HAS_THREADS:
+ @wrap_exceptions
+ def threads(self):
+ rawlist = cext.proc_threads(self.pid)
+ retlist = []
+ for thread_id, utime, stime in rawlist:
+ ntuple = _common.pthread(thread_id, utime, stime)
+ retlist.append(ntuple)
+ # The underlying C implementation retrieves all OS threads
+ # and filters them by PID. At this point we can't tell whether
+ # an empty list means there were no connections for process or
+ # process is no longer active so we force NSP in case the PID
+ # is no longer there.
+ if not retlist:
+ # will raise NSP if process is gone
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+ return retlist
+
+ @wrap_exceptions
+ def connections(self, kind='inet'):
+ ret = net_connections(kind, _pid=self.pid)
+ # The underlying C implementation retrieves all OS connections
+ # and filters them by PID. At this point we can't tell whether
+ # an empty list means there were no connections for process or
+ # process is no longer active so we force NSP in case the PID
+ # is no longer there.
+ if not ret:
+ # will raise NSP if process is gone
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+ return ret
+
+ @wrap_exceptions
+ def nice_get(self):
+ return cext_posix.getpriority(self.pid)
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ return cext_posix.setpriority(self.pid, value)
+
+ @wrap_exceptions
+ def ppid(self):
+ self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
+ return self._ppid
+
+ @wrap_exceptions
+ def uids(self):
+ real, effective, saved, _, _, _ = self._proc_cred()
+ return _common.puids(real, effective, saved)
+
+ @wrap_exceptions
+ def gids(self):
+ _, _, _, real, effective, saved = self._proc_cred()
+ return _common.puids(real, effective, saved)
+
+ @wrap_exceptions
+ def cpu_times(self):
+ cpu_times = cext.proc_cpu_times(self.pid, self._procfs_path)
+ return _common.pcputimes(*cpu_times)
+
+ @wrap_exceptions
+ def terminal(self):
+ ttydev = self._proc_basic_info()[proc_info_map['ttynr']]
+ # convert from 64-bit dev_t to 32-bit dev_t and then map the device
+ ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
+ # try to match rdev of /dev/pts/* files ttydev
+ for dev in glob.glob("/dev/**/*"):
+ if os.stat(dev).st_rdev == ttydev:
+ return dev
+ return None
+
+ @wrap_exceptions
+ def cwd(self):
+ procfs_path = self._procfs_path
+ try:
+ result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid))
+ return result.rstrip('/')
+ except OSError as err:
+ if err.errno == errno.ENOENT:
+ os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
+ return None
+ raise
+
+ @wrap_exceptions
+ def memory_info(self):
+ ret = self._proc_basic_info()
+ rss = ret[proc_info_map['rss']] * 1024
+ vms = ret[proc_info_map['vms']] * 1024
+ return pmem(rss, vms)
+
+ memory_full_info = memory_info
+
+ @wrap_exceptions
+ def status(self):
+ code = self._proc_basic_info()[proc_info_map['status']]
+ # XXX is '?' legit? (we're not supposed to return it anyway)
+ return PROC_STATUSES.get(code, '?')
+
+ def open_files(self):
+ # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then
+ # find matching name of the inode)
+ p = subprocess.Popen(["/usr/bin/procfiles", "-n", str(self.pid)],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if "no such process" in stderr.lower():
+ raise NoSuchProcess(self.pid, self._name)
+ procfiles = re.findall("(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout)
+ retlist = []
+ for fd, path in procfiles:
+ path = path.strip()
+ if path.startswith("//"):
+ path = path[1:]
+ if path.lower() == "cannot be retrieved":
+ continue
+ retlist.append(_common.popenfile(path, int(fd)))
+ return retlist
+
+ @wrap_exceptions
+ def num_fds(self):
+ if self.pid == 0: # no /proc/0/fd
+ return 0
+ return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
+
+ @wrap_exceptions
+ def num_ctx_switches(self):
+ return _common.pctxsw(
+ *cext.proc_num_ctx_switches(self.pid))
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ return _psposix.wait_pid(self.pid, timeout, self._name)
+
+ @wrap_exceptions
+ def io_counters(self):
+ try:
+ rc, wc, rb, wb = cext.proc_io_counters(self.pid)
+ except OSError:
+ # if process is terminated, proc_io_counters returns OSError
+ # instead of NSP
+ if not pid_exists(self.pid):
+ raise NoSuchProcess(self.pid, self._name)
+ raise
+ return _common.pio(rc, wc, rb, wb)
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index da264ef3..c2c926e2 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -27,6 +27,9 @@ from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
from ._compat import which
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = []
@@ -128,12 +131,6 @@ kinfo_proc_map = dict(
name=24,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -394,9 +391,12 @@ def net_connections(kind):
# have a very short lifetime so maybe the kernel
# can't initialize their status?
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
+ if fam in (AF_INET, AF_INET6):
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
type = socktype_to_enum(type)
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
ret.add(nt)
@@ -719,8 +719,10 @@ class Process(object):
except KeyError:
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
@@ -737,8 +739,10 @@ class Process(object):
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
status = TCP_STATUSES[status]
@@ -753,10 +757,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def nice_get(self):
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index b55f4197..06f1aa6b 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -41,6 +41,9 @@ from ._compat import b
from ._compat import basestring
from ._compat import long
from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
if sys.version_info >= (3, 4):
import enum
@@ -137,12 +140,6 @@ TCP_STATUSES = {
"0B": _common.CONN_CLOSING
}
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -1142,21 +1139,22 @@ def sensors_temperatures():
basenames = sorted(set([x.split('_')[0] for x in basenames]))
for base in basenames:
+ try:
+ current = float(cat(base + '_input')) / 1000.0
+ except (IOError, OSError) as err:
+ # A lot of things can go wrong here, so let's just skip the
+ # whole entry.
+ # https://github.com/giampaolo/psutil/issues/1009
+ # https://github.com/giampaolo/psutil/issues/1101
+ # https://github.com/giampaolo/psutil/issues/1129
+ warnings.warn("ignoring %r" % err, RuntimeWarning)
+ continue
+
unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
binary=False)
high = cat(base + '_max', fallback=None)
critical = cat(base + '_crit', fallback=None)
label = cat(base + '_label', fallback='', binary=False)
- try:
- current = float(cat(base + '_input')) / 1000.0
- except OSError as err:
- # https://github.com/giampaolo/psutil/issues/1009
- # https://github.com/giampaolo/psutil/issues/1101
- if err.errno in (errno.EIO, errno.ENODEV):
- warnings.warn("ignoring %r" % err, RuntimeWarning)
- continue
- else:
- raise
if high is not None:
high = float(high) / 1000.0
@@ -1169,8 +1167,8 @@ def sensors_temperatures():
def sensors_fans():
- """Return hardware (CPU and others) fans as a dict
- including hardware label, current speed.
+ """Return hardware fans info (for CPU and other peripherals) as a
+ dict including hardware label and current speed.
Implementation notes:
- /sys/class/hwmon looks like the most recent interface to
@@ -1187,11 +1185,14 @@ def sensors_fans():
basenames = sorted(set([x.split('_')[0] for x in basenames]))
for base in basenames:
+ try:
+ current = int(cat(base + '_input'))
+ except (IOError, OSError) as err:
+ warnings.warn("ignoring %r" % err, RuntimeWarning)
+ continue
unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
binary=False)
label = cat(base + '_label', fallback='', binary=False)
- current = int(cat(base + '_input'))
-
ret[unit_name].append(_common.sfan(label, current))
return dict(ret)
@@ -1355,6 +1356,30 @@ def pid_exists(pid):
return pid in pids()
+def ppid_map():
+ """Obtain a {pid: ppid, ...} dict for all running processes in
+ one shot. Used to speed up Process.children().
+ """
+ ret = {}
+ procfs_path = get_procfs_path()
+ for pid in pids():
+ try:
+ with open_binary("%s/%s/stat" % (procfs_path, pid)) as f:
+ data = f.read()
+ except EnvironmentError as err:
+ # Note: we should be able to access /stat for all processes
+ # so we won't bump into EPERM, which is good.
+ if err.errno not in (errno.ENOENT, errno.ESRCH,
+ errno.EPERM, errno.EACCES):
+ raise
+ else:
+ rpar = data.rfind(b')')
+ dset = data[rpar + 2:].split()
+ ppid = int(dset[1])
+ ret[pid] = ppid
+ return ret
+
+
def wrap_exceptions(fun):
"""Decorator which translates bare OSError and IOError exceptions
into NoSuchProcess and AccessDenied.
@@ -1470,9 +1495,17 @@ class Process(object):
if not data:
# may happen in case of zombie process
return []
- if data.endswith('\x00'):
+ # 'man proc' states that args are separated by null bytes '\0'
+ # and last char is supposed to be a null byte. Nevertheless
+ # some processes may change their cmdline after being started
+ # (via setproctitle() or similar), they are usually not
+ # compliant with this rule and use spaces instead. Google
+ # Chrome process is an example. See:
+ # https://github.com/giampaolo/psutil/issues/1179
+ sep = '\x00' if data.endswith('\x00') else ' '
+ if data.endswith(sep):
data = data[:-1]
- return [x for x in data.split('\x00')]
+ return [x for x in data.split(sep)]
@wrap_exceptions
def environ(self):
@@ -1532,10 +1565,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def create_time(self):
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index a38efc5b..f149980f 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -23,6 +23,9 @@ from ._common import parse_environ_block
from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = []
@@ -85,12 +88,6 @@ pidtaskinfo_map = dict(
volctxsw=7,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -212,6 +209,29 @@ def disk_partitions(all=False):
# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_battery():
+ """Return battery information.
+ """
+ try:
+ percent, minsleft, power_plugged = cext.sensors_battery()
+ except NotImplementedError:
+ # no power source - return None according to interface
+ return None
+ power_plugged = power_plugged == 1
+ if power_plugged:
+ secsleft = _common.POWER_TIME_UNLIMITED
+ elif minsleft == -1:
+ secsleft = _common.POWER_TIME_UNKNOWN
+ else:
+ secsleft = minsleft * 60
+ return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
# --- network
# =====================================================================
@@ -282,7 +302,22 @@ def users():
# =====================================================================
-pids = cext.pids
+def pids():
+ ls = cext.pids()
+ if 0 not in ls:
+ # On certain OSX versions pids() C doesn't return PID 0 but
+ # "ps" does and the process is querable via sysctl():
+ # https://travis-ci.org/giampaolo/psutil/jobs/309619941
+ try:
+ Process(0).create_time()
+ ls.append(0)
+ except NoSuchProcess:
+ pass
+ except AccessDenied:
+ ls.append(0)
+ return ls
+
+
pid_exists = _psposix.pid_exists
@@ -503,10 +538,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def nice_get(self):
diff --git a/psutil/_psposix.py b/psutil/_psposix.py
index 66d81a3d..6bb8444d 100644
--- a/psutil/_psposix.py
+++ b/psutil/_psposix.py
@@ -15,14 +15,10 @@ from ._common import sdiskusage
from ._common import usage_percent
from ._compat import PY3
from ._compat import unicode
+from ._exceptions import TimeoutExpired
-__all__ = ['TimeoutExpired', 'pid_exists', 'wait_pid', 'disk_usage',
- 'get_terminal_map']
-
-
-class TimeoutExpired(Exception):
- pass
+__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
def pid_exists(pid):
@@ -53,7 +49,7 @@ def pid_exists(pid):
return True
-def wait_pid(pid, timeout=None):
+def wait_pid(pid, timeout=None, proc_name=None):
"""Wait for process with pid 'pid' to terminate and return its
exit status code as an integer.
@@ -67,7 +63,7 @@ def wait_pid(pid, timeout=None):
def check_timeout(delay):
if timeout is not None:
if timer() >= stop_at:
- raise TimeoutExpired()
+ raise TimeoutExpired(timeout, pid=pid, name=proc_name)
time.sleep(delay)
return min(delay * 2, 0.04)
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 9931d885..5471d5aa 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -24,6 +24,9 @@ from ._common import socktype_to_enum
from ._common import usage_percent
from ._compat import b
from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
@@ -78,12 +81,6 @@ proc_info_map = dict(
status=6,
ttynr=7)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -266,8 +263,10 @@ def net_connections(kind, _pid=-1):
if type_ not in types:
continue
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type_ = socktype_to_enum(type_)
@@ -723,7 +722,4 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
diff --git a/psutil/_psutil_aix.c b/psutil/_psutil_aix.c
new file mode 100644
index 00000000..916254d5
--- /dev/null
+++ b/psutil/_psutil_aix.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * AIX support is experimental at this time.
+ * The following functions and methods are unsupported on the AIX platform:
+ * - psutil.Process.memory_maps
+ *
+ * Known limitations:
+ * - psutil.Process.io_counters read count is always 0
+ * - psutil.Process.threads may not be available on older AIX versions
+ * - reading basic process info may fail or return incorrect values when
+ * process is starting (see IBM APAR IV58499 - fixed in newer AIX versions)
+ * - sockets and pipes may not be counted in num_fds (fixed in newer AIX
+ * versions)
+ *
+ * Useful resources:
+ * - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/
+ * ssw_aix_61/com.ibm.aix.files/proc.htm
+ * - libperfstat: http://www-01.ibm.com/support/knowledgecenter/
+ * ssw_aix_61/com.ibm.aix.files/libperfstat.h.htm
+ */
+
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/sysinfo.h>
+#include <sys/procfs.h>
+#include <sys/socket.h>
+#include <sys/thread.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/tihdr.h>
+#include <stropts.h>
+#include <netinet/tcp_fsm.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <libperfstat.h>
+
+#include "arch/aix/ifaddrs.h"
+#include "arch/aix/net_connections.h"
+#include "arch/aix/common.h"
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
+
+
+#define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)
+
+/*
+ * Read a file content and fills a C structure with it.
+ */
+int
+psutil_file_to_struct(char *path, void *fstruct, size_t size) {
+ int fd;
+ size_t nbytes;
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+ return 0;
+ }
+ nbytes = read(fd, fstruct, size);
+ if (nbytes <= 0) {
+ close(fd);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 0;
+ }
+ if (nbytes != size) {
+ close(fd);
+ PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
+ return 0;
+ }
+ close(fd);
+ return nbytes;
+}
+
+
+/*
+ * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_basic_info(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ psinfo_t info;
+ pstatus_t status;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+
+ sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+
+ if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {
+ // From the /proc docs: "If the process is a zombie, the pr_nlwp
+ // and pr_lwp.pr_lwpid flags are zero."
+ status.pr_stat = SZOMB;
+ } else if (info.pr_flag & SEXIT) {
+ // "exiting" processes don't have /proc/<pid>/status
+ // There are other "exiting" processes that 'ps' shows as "active"
+ status.pr_stat = SACTIVE;
+ } else {
+ sprintf(path, "%s/%i/status", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKdiiiK",
+ (unsigned long long) info.pr_ppid, // parent pid
+ (unsigned long long) info.pr_rssize, // rss
+ (unsigned long long) info.pr_size, // vms
+ TV2DOUBLE(info.pr_start), // create time
+ (int) info.pr_lwp.pr_nice, // nice
+ (int) info.pr_nlwp, // no. of threads
+ (int) status.pr_stat, // status code
+ (unsigned long long)info.pr_ttydev // tty nr
+ );
+}
+
+
+/*
+ * Return process name and args as a Python tuple.
+ */
+static PyObject *
+psutil_proc_name_and_args(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ psinfo_t info;
+ const char *procfs_path;
+ PyObject *py_name = NULL;
+ PyObject *py_args = NULL;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+
+ py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
+ if (!py_name)
+ goto error;
+ py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
+ if (!py_args)
+ goto error;
+ py_retlist = Py_BuildValue("OO", py_name, py_args);
+ if (!py_retlist)
+ goto error;
+ Py_DECREF(py_name);
+ Py_DECREF(py_args);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_name);
+ Py_XDECREF(py_args);
+ Py_XDECREF(py_retlist);
+ return NULL;
+}
+
+
+#ifdef CURR_VERSION_THREAD
+/*
+ * Retrieves all threads used by process returning a list of tuples
+ * including thread id, user time and system time.
+ */
+static PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ long pid;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ perfstat_thread_t *threadt = NULL;
+ perfstat_id_t id;
+ int i, rc, thread_count;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+
+ /* Get the count of threads */
+ thread_count = perfstat_thread(NULL, NULL, sizeof(perfstat_thread_t), 0);
+ if (thread_count <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* Allocate enough memory */
+ threadt = (perfstat_thread_t *)calloc(thread_count,
+ sizeof(perfstat_thread_t));
+ if (threadt == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_thread(&id, threadt, sizeof(perfstat_thread_t),
+ thread_count);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < thread_count; i++) {
+ if (threadt[i].pid != pid)
+ continue;
+
+ py_tuple = Py_BuildValue("Idd",
+ threadt[i].tid,
+ threadt[i].ucpu_time,
+ threadt[i].scpu_time);
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ free(threadt);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (threadt != NULL)
+ free(threadt);
+ return NULL;
+}
+#endif
+
+
+static PyObject *
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
+ long pid;
+ int rc;
+ perfstat_process_t procinfo;
+ perfstat_id_t id;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+
+ snprintf(id.name, sizeof(id.name), "%ld", pid);
+ rc = perfstat_process(&id, &procinfo, sizeof(perfstat_process_t), 1);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("(KKKK)",
+ procinfo.inOps, // XXX always 0
+ procinfo.outOps,
+ procinfo.inBytes, // XXX always 0
+ procinfo.outBytes);
+}
+
+
+/*
+ * Return process user and system CPU times as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ pstatus_t info;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/status", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+ // results are more precise than os.times()
+ return Py_BuildValue("dddd",
+ TV2DOUBLE(info.pr_utime),
+ TV2DOUBLE(info.pr_stime),
+ TV2DOUBLE(info.pr_cutime),
+ TV2DOUBLE(info.pr_cstime));
+}
+
+
+/*
+ * Return process uids/gids as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cred(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ prcred_t info;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/cred", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+ return Py_BuildValue("iiiiii",
+ info.pr_ruid, info.pr_euid, info.pr_suid,
+ info.pr_rgid, info.pr_egid, info.pr_sgid);
+}
+
+
+/*
+ * Return process voluntary and involuntary context switches as a Python tuple.
+ */
+static PyObject *
+psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
+ PyObject *py_tuple = NULL;
+ pid32_t requested_pid;
+ pid32_t pid = 0;
+ int np = 0;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ struct procentry64 *p;
+
+ if (! PyArg_ParseTuple(args, "i", &requested_pid))
+ return NULL;
+
+ processes = psutil_read_process_table(&np);
+ if (!processes)
+ return NULL;
+
+ /* Loop through processes */
+ for (p = processes; np > 0; np--, p++) {
+ pid = p->pi_pid;
+ if (requested_pid != pid)
+ continue;
+ py_tuple = Py_BuildValue("LL",
+ (long long) p->pi_ru.ru_nvcsw, /* voluntary context switches */
+ (long long) p->pi_ru.ru_nivcsw); /* involuntary */
+ free(processes);
+ return py_tuple;
+ }
+
+ /* finished iteration without finding requested pid */
+ free(processes);
+ return NoSuchProcess("");
+}
+
+
+/*
+ * Return users currently connected on the system.
+ */
+static PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ struct utmpx *ut;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_username = NULL;
+ PyObject *py_tty = NULL;
+ PyObject *py_hostname = NULL;
+ PyObject *py_user_proc = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ setutxent();
+ while (NULL != (ut = getutxent())) {
+ if (ut->ut_type == USER_PROCESS)
+ py_user_proc = Py_True;
+ else
+ py_user_proc = Py_False;
+ py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
+ if (! py_username)
+ goto error;
+ py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
+ if (! py_tty)
+ goto error;
+ py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
+ if (! py_hostname)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOOfOi)",
+ py_username, // username
+ py_tty, // tty
+ py_hostname, // hostname
+ (float)ut->ut_tv.tv_sec, // tstamp
+ py_user_proc, // (bool) user process
+ ut->ut_pid // process id
+ );
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_username);
+ Py_DECREF(py_tty);
+ Py_DECREF(py_hostname);
+ Py_DECREF(py_tuple);
+ }
+ endutxent();
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tty);
+ Py_XDECREF(py_hostname);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (ut != NULL)
+ endutxent();
+ return NULL;
+}
+
+
+/*
+ * Return disk mounted partitions as a list of tuples including device,
+ * mount point and filesystem type.
+ */
+static PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ FILE *file = NULL;
+ struct mntent * mt = NULL;
+ PyObject *py_dev = NULL;
+ PyObject *py_mountp = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ file = setmntent(MNTTAB, "rb");
+ if (file == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ mt = getmntent(file);
+ while (mt != NULL) {
+ py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ mt->mnt_type, // fs type
+ mt->mnt_opts); // options
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_dev);
+ Py_DECREF(py_mountp);
+ Py_DECREF(py_tuple);
+ mt = getmntent(file);
+ }
+ endmntent(file);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (file != NULL)
+ endmntent(file);
+ return NULL;
+}
+
+
+/*
+ * Return a list of tuples for network I/O statistics.
+ */
+static PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ perfstat_netinterface_t *statp = NULL;
+ int tot, i;
+ perfstat_id_t first;
+
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_ifc_info = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ /* check how many perfstat_netinterface_t structures are available */
+ tot = perfstat_netinterface(
+ NULL, NULL, sizeof(perfstat_netinterface_t), 0);
+ if (tot == 0) {
+ // no network interfaces - return empty dict
+ return py_retdict;
+ }
+ if (tot < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ statp = (perfstat_netinterface_t *)
+ malloc(tot * sizeof(perfstat_netinterface_t));
+ if (statp == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ strcpy(first.name, FIRST_NETINTERFACE);
+ tot = perfstat_netinterface(&first, statp,
+ sizeof(perfstat_netinterface_t), tot);
+ if (tot < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < tot; i++) {
+ py_ifc_info = Py_BuildValue("(KKKKKKKK)",
+ statp[i].obytes, /* number of bytes sent on interface */
+ statp[i].ibytes, /* number of bytes received on interface */
+ statp[i].opackets, /* number of packets sent on interface */
+ statp[i].ipackets, /* number of packets received on interface */
+ statp[i].ierrors, /* number of input errors on interface */
+ statp[i].oerrors, /* number of output errors on interface */
+ statp[i].if_iqdrops, /* Dropped on input, this interface */
+ statp[i].xmitdrops /* number of packets not transmitted */
+ );
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, statp[i].name, py_ifc_info))
+ goto error;
+ Py_DECREF(py_ifc_info);
+ }
+
+ free(statp);
+ return py_retdict;
+
+error:
+ if (statp != NULL)
+ free(statp);
+ Py_XDECREF(py_ifc_info);
+ Py_DECREF(py_retdict);
+ return NULL;
+}
+
+
+static PyObject*
+psutil_net_if_stats(PyObject* self, PyObject* args) {
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ int mtu;
+ struct ifreq ifr;
+ PyObject *py_is_up = NULL;
+ PyObject *py_retlist = NULL;
+
+ 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));
+
+ // 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;
+
+ close(sock);
+ py_retlist = Py_BuildValue("[Oi]", py_is_up, mtu);
+ if (!py_retlist)
+ goto error;
+ Py_DECREF(py_is_up);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_is_up);
+ if (sock != 0)
+ close(sock);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
+static PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ float boot_time = 0.0;
+ struct utmpx *ut;
+
+ setutxent();
+ while (NULL != (ut = getutxent())) {
+ if (ut->ut_type == BOOT_TIME) {
+ boot_time = (float)ut->ut_tv.tv_sec;
+ break;
+ }
+ }
+ endutxent();
+ if (boot_time == 0.0) {
+ /* could not find BOOT_TIME in getutxent loop */
+ PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
+ return NULL;
+ }
+ return Py_BuildValue("f", boot_time);
+}
+
+
+/*
+ * Return a Python list of tuple representing per-cpu times
+ */
+static PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ int ncpu, rc, i;
+ perfstat_cpu_t *cpu = NULL;
+ perfstat_id_t id;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ /* get the number of cpus in ncpu */
+ ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+ if (ncpu <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* allocate enough memory to hold the ncpu structures */
+ cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+ if (cpu == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < ncpu; i++) {
+ py_cputime = Py_BuildValue(
+ "(dddd)",
+ (double)cpu[i].user,
+ (double)cpu[i].sys,
+ (double)cpu[i].idle,
+ (double)cpu[i].wait);
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_DECREF(py_cputime);
+ }
+ free(cpu);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ if (cpu != NULL)
+ free(cpu);
+ return NULL;
+}
+
+
+/*
+ * Return disk IO statistics.
+ */
+static PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_disk_info = NULL;
+ perfstat_disk_t *diskt = NULL;
+ perfstat_id_t id;
+ int i, rc, disk_count;
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ /* Get the count of disks */
+ disk_count = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
+ if (disk_count <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* Allocate enough memory */
+ diskt = (perfstat_disk_t *)calloc(disk_count,
+ sizeof(perfstat_disk_t));
+ if (diskt == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, FIRST_DISK);
+ rc = perfstat_disk(&id, diskt, sizeof(perfstat_disk_t),
+ disk_count);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < disk_count; i++) {
+ py_disk_info = Py_BuildValue(
+ "KKKKKK",
+ diskt[i].__rxfers,
+ diskt[i].xfers - diskt[i].__rxfers,
+ diskt[i].rblks * diskt[i].bsize,
+ diskt[i].wblks * diskt[i].bsize,
+ diskt[i].rserv / 1000 / 1000, // from nano to milli secs
+ diskt[i].wserv / 1000 / 1000 // from nano to milli secs
+ );
+ if (py_disk_info == NULL)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, diskt[i].name,
+ py_disk_info))
+ goto error;
+ Py_DECREF(py_disk_info);
+ }
+ free(diskt);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ if (diskt != NULL)
+ free(diskt);
+ return NULL;
+}
+
+
+/*
+ * Return virtual memory usage statistics.
+ */
+static PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ int rc;
+ int pagesize = getpagesize();
+ perfstat_memory_total_t memory;
+
+ rc = perfstat_memory_total(
+ NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+ if (rc <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKKK",
+ (unsigned long long) memory.real_total * pagesize,
+ (unsigned long long) memory.real_avail * pagesize,
+ (unsigned long long) memory.real_free * pagesize,
+ (unsigned long long) memory.real_pinned * pagesize,
+ (unsigned long long) memory.real_inuse * pagesize
+ );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+static PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ int rc;
+ int pagesize = getpagesize();
+ perfstat_memory_total_t memory;
+
+ rc = perfstat_memory_total(
+ NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+ if (rc <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKK",
+ (unsigned long long) memory.pgsp_total * pagesize,
+ (unsigned long long) memory.pgsp_free * pagesize,
+ (unsigned long long) memory.pgins * pagesize,
+ (unsigned long long) memory.pgouts * pagesize
+ );
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+ int ncpu, rc, i;
+ // perfstat_cpu_total_t doesn't have invol/vol cswitch, only pswitch
+ // which is apparently something else. We have to sum over all cpus
+ perfstat_cpu_t *cpu = NULL;
+ perfstat_id_t id;
+ u_longlong_t cswitches = 0;
+ u_longlong_t devintrs = 0;
+ u_longlong_t softintrs = 0;
+ u_longlong_t syscall = 0;
+
+ /* get the number of cpus in ncpu */
+ ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+ if (ncpu <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* allocate enough memory to hold the ncpu structures */
+ cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+ if (cpu == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < ncpu; i++) {
+ cswitches += cpu[i].invol_cswitch + cpu[i].vol_cswitch;
+ devintrs += cpu[i].devintrs;
+ softintrs += cpu[i].softintrs;
+ syscall += cpu[i].syscall;
+ }
+
+ free(cpu);
+
+ return Py_BuildValue(
+ "KKKK",
+ cswitches,
+ devintrs,
+ softintrs,
+ syscall
+ );
+
+error:
+ if (cpu != NULL)
+ free(cpu);
+ return NULL;
+}
+
+
+/*
+ * define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef
+PsutilMethods[] =
+{
+ // --- process-related functions
+ {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
+ "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
+ {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS,
+ "Return process name and args."},
+ {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
+ "Return process user and system CPU times."},
+ {"proc_cred", psutil_proc_cred, METH_VARARGS,
+ "Return process uids/gids."},
+#ifdef CURR_VERSION_THREAD
+ {"proc_threads", psutil_proc_threads, METH_VARARGS,
+ "Return process threads"},
+#endif
+ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
+ "Get process I/O counters."},
+ {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
+ "Get process I/O counters."},
+
+ // --- system-related functions
+ {"users", psutil_users, METH_VARARGS,
+ "Return currently connected users."},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
+ "Return disk partitions."},
+ {"boot_time", psutil_boot_time, METH_VARARGS,
+ "Return system boot time in seconds since the EPOCH."},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
+ "Return system per-cpu times as a list of tuples"},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
+ "Return a Python dict of tuples for disk I/O statistics."},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
+ "Return system virtual memory usage statistics"},
+ {"swap_mem", psutil_swap_mem, METH_VARARGS,
+ "Return stats about swap memory, in bytes"},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
+ "Return a Python dict of tuples for network I/O statistics."},
+ {"net_connections", psutil_net_connections, METH_VARARGS,
+ "Return system-wide connections"},
+ {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+ "Return NIC stats (isup, mtu)"},
+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+ "Return CPU statistics"},
+
+ // --- others
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+
+struct module_state {
+ PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+#else
+#define GETSTATE(m) (&_state)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+static int
+psutil_aix_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int
+psutil_aix_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "psutil_aix",
+ NULL,
+ sizeof(struct module_state),
+ PsutilMethods,
+ NULL,
+ psutil_aix_traverse,
+ psutil_aix_clear,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__psutil_aix(void)
+
+#else
+#define INITERROR return
+
+void init_psutil_aix(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *module = PyModule_Create(&moduledef);
+#else
+ PyObject *module = Py_InitModule("_psutil_aix", PsutilMethods);
+#endif
+ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+
+ PyModule_AddIntConstant(module, "SIDL", SIDL);
+ PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+ PyModule_AddIntConstant(module, "SACTIVE", SACTIVE);
+ PyModule_AddIntConstant(module, "SSWAP", SSWAP);
+ PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+
+ PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED);
+ PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING);
+ PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT);
+ PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN);
+ PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED);
+ PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
+ PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RECEIVED);
+ PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1);
+ PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2);
+ PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
+ PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
+ PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+
+ psutil_setup();
+
+ if (module == NULL)
+ INITERROR;
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index 64e1e7f7..09ae0c30 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -475,7 +475,7 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -986,8 +986,8 @@ PsutilMethods[] = {
#endif
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1090,6 +1090,8 @@ void init_psutil_bsd(void)
// PSUTIL_CONN_NONE
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", 128);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index dace4724..908dbf14 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -9,14 +9,41 @@
#include <Python.h>
#include <stdio.h>
+
+// Global vars.
+int PSUTIL_DEBUG = 0;
+int PSUTIL_TESTING = 0;
+
+
+/*
+ * Backport of unicode FS APIs from Python 3.
+ * On Python 2 we just return a plain byte string
+ * which is never supposed to raise decoding errors.
+ * See: https://github.com/giampaolo/psutil/issues/1040
+ */
+#if PY_MAJOR_VERSION < 3
+PyObject *
+PyUnicode_DecodeFSDefault(char *s) {
+ return PyString_FromString(s);
+}
+
+
+PyObject *
+PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) {
+ return PyString_FromStringAndSize(s, size);
+}
+#endif
+
+
/*
* Set OSError(errno=ESRCH, strerror="No such process") Python exception.
+ * If msg != "" the exception message will change in accordance.
*/
PyObject *
-NoSuchProcess(void) {
+NoSuchProcess(char *msg) {
PyObject *exc;
- char *msg = strerror(ESRCH);
- exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
+ exc = PyObject_CallFunction(
+ PyExc_OSError, "(is)", ESRCH, strlen(msg) ? msg : strerror(ESRCH));
PyErr_SetObject(PyExc_OSError, exc);
Py_XDECREF(exc);
return NULL;
@@ -25,63 +52,53 @@ NoSuchProcess(void) {
/*
* Set OSError(errno=EACCES, strerror="Permission denied") Python exception.
+ * If msg != "" the exception message will change in accordance.
*/
PyObject *
-AccessDenied(void) {
+AccessDenied(char *msg) {
PyObject *exc;
- char *msg = strerror(EACCES);
- exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
+ exc = PyObject_CallFunction(
+ PyExc_OSError, "(is)", EACCES, strlen(msg) ? msg : strerror(EACCES));
PyErr_SetObject(PyExc_OSError, exc);
Py_XDECREF(exc);
return NULL;
}
-static int _psutil_testing = -1;
-
-
/*
- * Return 1 if PSUTIL_TESTING env var is set else 0.
+ * Enable testing mode. This has the same effect as setting PSUTIL_TESTING
+ * env var. This dual method exists because updating os.environ on
+ * Windows has no effect. Called on unit tests setup.
*/
-int
-psutil_testing(void) {
- if (_psutil_testing == -1) {
- if (getenv("PSUTIL_TESTING") != NULL)
- _psutil_testing = 1;
- else
- _psutil_testing = 0;
- }
- return _psutil_testing;
+PyObject *
+psutil_set_testing(PyObject *self, PyObject *args) {
+ PSUTIL_TESTING = 1;
+ Py_INCREF(Py_None);
+ return Py_None;
}
/*
- * Return True if PSUTIL_TESTING env var is set else False.
+ * Print a debug message on stderr. No-op if PSUTIL_DEBUG env var is not set.
*/
-PyObject *
-py_psutil_testing(PyObject *self, PyObject *args) {
- PyObject *res;
- res = psutil_testing() ? Py_True : Py_False;
- Py_INCREF(res);
- return res;
+void
+psutil_debug(const char* format, ...) {
+ va_list argptr;
+ va_start(argptr, format);
+ fprintf(stderr, "psutil-dubug> ");
+ vfprintf(stderr, format, argptr);
+ fprintf(stderr, "\n");
+ va_end(argptr);
}
/*
- * Backport of unicode FS APIs from Python 3.
- * On Python 2 we just return a plain byte string
- * which is never supposed to raise decoding errors.
- * See: https://github.com/giampaolo/psutil/issues/1040
+ * Called on module import on all platforms.
*/
-#if PY_MAJOR_VERSION < 3
-PyObject *
-PyUnicode_DecodeFSDefault(char *s) {
- return PyString_FromString(s);
-}
-
-
-PyObject *
-PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) {
- return PyString_FromStringAndSize(s, size);
+void
+psutil_setup(void) {
+ if (getenv("PSUTIL_DEBUG") != NULL)
+ PSUTIL_DEBUG = 1;
+ if (getenv("PSUTIL_TESTING") != NULL)
+ PSUTIL_TESTING = 1;
}
-#endif
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 13404532..3db3f5ed 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -6,14 +6,20 @@
#include <Python.h>
+extern int PSUTIL_TESTING;
+extern int PSUTIL_DEBUG;
+
// a signaler for connections without an actual status
static const int PSUTIL_CONN_NONE = 128;
-PyObject* AccessDenied(void);
-PyObject* NoSuchProcess(void);
-int psutil_testing(void);
-PyObject* py_psutil_testing(PyObject *self, PyObject *args);
#if PY_MAJOR_VERSION < 3
PyObject* PyUnicode_DecodeFSDefault(char *s);
PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size);
#endif
+
+PyObject* AccessDenied(char *msg);
+PyObject* NoSuchProcess(char *msg);
+
+PyObject* psutil_set_testing(PyObject *self, PyObject *args);
+void psutil_debug(const char* format, ...);
+void psutil_setup(void);
diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c
index a15ebe5c..d1f0d145 100644
--- a/psutil/_psutil_linux.c
+++ b/psutil/_psutil_linux.c
@@ -479,7 +479,7 @@ psutil_users(PyObject *self, PyObject *args) {
"(OOOfOi)",
py_username, // username
py_tty, // tty
- py_username, // hostname
+ py_hostname, // hostname
(float)ut->ut_tv.tv_sec, // tstamp
py_user_proc, // (bool) user process
ut->ut_pid // process id
@@ -607,8 +607,8 @@ PsutilMethods[] = {
#endif
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -713,6 +713,8 @@ void init_psutil_linux(void)
PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL);
PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c
index 6e37bca5..f43bb010 100644
--- a/psutil/_psutil_osx.c
+++ b/psutil/_psutil_osx.c
@@ -38,6 +38,8 @@
#include <IOKit/storage/IOBlockStorageDriver.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
#include "_psutil_common.h"
#include "_psutil_posix.h"
@@ -273,9 +275,9 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
ret = proc_pidpath((pid_t)pid, &buf, sizeof(buf));
if (ret == 0) {
if (pid == 0)
- AccessDenied();
+ AccessDenied("");
else
- psutil_raise_for_pid(pid, "proc_pidpath() syscall failed");
+ psutil_raise_for_pid(pid, "proc_pidpath()");
return NULL;
}
return PyUnicode_DecodeFSDefault(buf);
@@ -345,7 +347,16 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
- psutil_raise_for_pid(pid, "task_for_pid() failed");
+ if ((err == 5) && (errno == ENOENT)) {
+ // See: https://github.com/giampaolo/psutil/issues/1181
+ psutil_debug("task_for_pid(MACH_PORT_NULL) failed; err=%i, "
+ "errno=%i, msg='%s'\n", err, errno,
+ mach_error_string(err));
+ AccessDenied("");
+ }
+ else {
+ psutil_raise_for_pid(pid, "task_for_pid(MACH_PORT_NULL)");
+ }
goto error;
}
@@ -356,8 +367,15 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
err = vm_region_recurse_64(task, &address, &size, &depth,
(vm_region_info_64_t)&info, &count);
- if (err == KERN_INVALID_ADDRESS)
+ if (err == KERN_INVALID_ADDRESS) {
+ // TODO temporary
+ psutil_debug("vm_region_recurse_64 returned KERN_INVALID_ADDRESS");
break;
+ }
+ if (err != KERN_SUCCESS) {
+ psutil_debug("vm_region_recurse_64 returned != KERN_SUCCESS");
+ }
+
if (info.is_submap) {
depth++;
}
@@ -385,8 +403,9 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
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");
+ // TODO temporary
+ psutil_debug("proc_regionfilename() failed");
+ psutil_raise_for_pid(pid, "proc_regionfilename()");
goto error;
}
@@ -569,9 +588,9 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
+ NoSuchProcess("");
else
- AccessDenied();
+ AccessDenied("");
return NULL;
}
@@ -1016,9 +1035,9 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
+ NoSuchProcess("");
else
- AccessDenied();
+ AccessDenied("");
goto error;
}
@@ -1028,7 +1047,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (err != KERN_SUCCESS) {
// errcode 4 is "invalid argument" (access denied)
if (err == 4) {
- AccessDenied();
+ AccessDenied("");
}
else {
// otherwise throw a runtime error with appropriate error code
@@ -1138,7 +1157,6 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
for (i = 0; i < iterations; i++) {
- py_tuple = NULL;
fdp_pointer = &fds_pointer[i];
if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
@@ -1157,7 +1175,8 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
continue;
}
else {
- psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO)");
goto error;
}
}
@@ -1176,7 +1195,9 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_DECREF(py_tuple);
+ py_tuple = NULL;
Py_DECREF(py_path);
+ py_path = NULL;
// --- /construct python list
}
}
@@ -1267,7 +1288,8 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
continue;
}
else {
- psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)");
goto error;
}
}
@@ -1785,6 +1807,92 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
}
+/*
+ * Return battery information.
+ */
+static PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+ PyObject *py_tuple = NULL;
+ CFTypeRef power_info = NULL;
+ CFArrayRef power_sources_list = NULL;
+ CFDictionaryRef power_sources_information = NULL;
+ CFNumberRef capacity_ref = NULL;
+ CFNumberRef time_to_empty_ref = NULL;
+ CFStringRef ps_state_ref = NULL;
+ uint32_t capacity; /* units are percent */
+ int time_to_empty; /* units are minutes */
+ int is_power_plugged;
+
+ power_info = IOPSCopyPowerSourcesInfo();
+
+ if (!power_info) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesInfo() syscall failed");
+ goto error;
+ }
+
+ power_sources_list = IOPSCopyPowerSourcesList(power_info);
+ if (!power_sources_list) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesList() syscall failed");
+ goto error;
+ }
+
+ /* Should only get one source. But in practice, check for > 0 sources */
+ if (!CFArrayGetCount(power_sources_list)) {
+ PyErr_SetString(PyExc_NotImplementedError, "no battery");
+ goto error;
+ }
+
+ power_sources_information = IOPSGetPowerSourceDescription(
+ power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
+
+ capacity_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
+ if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "No battery capacity infomration in power sources info");
+ goto error;
+ }
+
+ ps_state_ref = (CFStringRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
+ is_power_plugged = CFStringCompare(
+ ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
+ == kCFCompareEqualTo;
+
+ time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
+ if (!CFNumberGetValue(time_to_empty_ref,
+ kCFNumberIntType, &time_to_empty)) {
+ /* This value is recommended for non-Apple power sources, so it's not
+ * an error if it doesn't exist. We'll return -1 for "unknown" */
+ /* A value of -1 indicates "Still Calculating the Time" also for
+ * apple power source */
+ time_to_empty = -1;
+ }
+
+ py_tuple = Py_BuildValue("Iii",
+ capacity, time_to_empty, is_power_plugged);
+ if (!py_tuple) {
+ goto error;
+ }
+
+ CFRelease(power_info);
+ CFRelease(power_sources_list);
+ /* Caller should NOT release power_sources_information */
+
+ return py_tuple;
+
+error:
+ if (power_info)
+ CFRelease(power_info);
+ if (power_sources_list)
+ CFRelease(power_sources_list);
+ Py_XDECREF(py_tuple);
+ return NULL;
+}
+
/*
* define the psutil C module methods and initialize the module.
@@ -1851,10 +1959,12 @@ PsutilMethods[] = {
"Return currently connected users as a list of tuples"},
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return CPU statistics"},
+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
+ "Return battery information."},
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1935,6 +2045,8 @@ init_psutil_osx(void)
PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index 80c1b8cb..cc827273 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -18,12 +18,15 @@
#ifdef PSUTIL_SUNOS10
#include "arch/solaris/v10/ifaddrs.h"
+#elif PSUTIL_AIX
+ #include "arch/aix/ifaddrs.h"
#else
#include <ifaddrs.h>
#endif
#if defined(PSUTIL_LINUX)
#include <netdb.h>
+ #include <linux/types.h>
#include <linux/if_packet.h>
#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
#include <netdb.h>
@@ -35,6 +38,8 @@
#elif defined(PSUTIL_SUNOS)
#include <netdb.h>
#include <sys/sockio.h>
+#elif defined(PSUTIL_AIX)
+ #include <netdb.h>
#endif
#include "_psutil_common.h"
@@ -107,16 +112,21 @@ psutil_pid_exists(long pid) {
* This will always set a Python exception and return NULL.
*/
int
-psutil_raise_for_pid(long pid, char *msg) {
+psutil_raise_for_pid(long pid, char *syscall_name) {
// Set exception to AccessDenied if pid exists else NoSuchProcess.
if (errno != 0) {
+ // Unlikely we get here.
PyErr_SetFromErrno(PyExc_OSError);
return 0;
}
- if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
- else
- PyErr_SetString(PyExc_RuntimeError, msg);
+ else if (psutil_pid_exists(pid) == 0) {
+ psutil_debug("%s syscall failed and PID %i no longer exists; "
+ "assume NoSuchProcess", syscall_name, pid);
+ NoSuchProcess("");
+ }
+ else {
+ PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall_name);
+ }
return 0;
}
@@ -688,7 +698,7 @@ void init_psutil_posix(void)
PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
#endif
-#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS)
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) || defined(PSUTIL_AIX)
PyModule_AddIntConstant(module, "AF_LINK", AF_LINK);
#endif
diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c
index 12caaec7..c6673642 100644
--- a/psutil/_psutil_sunos.c
+++ b/psutil/_psutil_sunos.c
@@ -9,13 +9,13 @@
* this in Cython which I later on translated in C.
*/
-// fix compilation issue on SunOS 5.10, see:
-// https://github.com/giampaolo/psutil/issues/421
-// https://github.com/giampaolo/psutil/issues/1077
-// http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt
-//
-// Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\
-//
+/* fix compilation issue on SunOS 5.10, see:
+ * https://github.com/giampaolo/psutil/issues/421
+ * https://github.com/giampaolo/psutil/issues/1077
+ * http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt
+ *
+ * Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\
+*/
#define NEW_MIB_COMPLIANT 1
#define _STRUCTURED_PROC 1
@@ -126,9 +126,9 @@ psutil_proc_name_and_args(PyObject *self, PyObject *args) {
char path[1000];
psinfo_t info;
const char *procfs_path;
- PyObject *py_name;
- PyObject *py_args;
- PyObject *py_retlist;
+ PyObject *py_name = NULL;
+ PyObject *py_args = NULL;
+ PyObject *py_retlist = NULL;
if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
return NULL;
@@ -185,7 +185,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
goto error;
if (! info.pr_envp) {
- AccessDenied();
+ AccessDenied("");
goto error;
}
@@ -328,7 +328,7 @@ psutil_proc_cpu_num(PyObject *self, PyObject *args) {
return Py_BuildValue("i", proc_num);
error:
- if (fd != NULL)
+ if (fd != -1)
close(fd);
if (ptr != NULL)
free(ptr);
@@ -360,7 +360,7 @@ psutil_proc_cred(PyObject *self, PyObject *args) {
/*
- * Return process uids/gids as a Python tuple.
+ * Return process voluntary and involuntary context switches as a Python tuple.
*/
static PyObject *
psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
@@ -548,6 +548,7 @@ psutil_users(PyObject *self, PyObject *args) {
if (py_retlist == NULL)
return NULL;
+ setutxent();
while (NULL != (ut = getutxent())) {
if (ut->ut_type == USER_PROCESS)
py_user_proc = Py_True;
@@ -580,7 +581,7 @@ psutil_users(PyObject *self, PyObject *args) {
Py_DECREF(py_hostname);
Py_DECREF(py_tuple);
}
- endutent();
+ endutxent();
return py_retlist;
@@ -590,8 +591,7 @@ error:
Py_XDECREF(py_hostname);
Py_XDECREF(py_tuple);
Py_DECREF(py_retlist);
- if (ut != NULL)
- endutent();
+ endutxent();
return NULL;
}
@@ -832,7 +832,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
pr_addr_sz = p->pr_vaddr + p->pr_size;
// perms
- sprintf(perms, "%c%c%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
+ sprintf(perms, "%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
p->pr_mflags & MA_WRITE ? 'w' : '-',
p->pr_mflags & MA_EXEC ? 'x' : '-',
p->pr_mflags & MA_SHARED ? 's' : '-');
@@ -1332,20 +1332,20 @@ psutil_boot_time(PyObject *self, PyObject *args) {
float boot_time = 0.0;
struct utmpx *ut;
+ setutxent();
while (NULL != (ut = getutxent())) {
if (ut->ut_type == BOOT_TIME) {
boot_time = (float)ut->ut_tv.tv_sec;
break;
}
}
- endutent();
- if (boot_time != 0.0) {
- return Py_BuildValue("f", boot_time);
- }
- else {
+ endutxent();
+ if (boot_time == 0.0) {
+ /* could not find BOOT_TIME in getutxent loop */
PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
return NULL;
}
+ return Py_BuildValue("f", boot_time);
}
@@ -1592,8 +1592,8 @@ PsutilMethods[] = {
"Return CPU statistics"},
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1679,6 +1679,8 @@ void init_psutil_sunos(void)
PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND);
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 0ed4f761..21738e8c 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -25,6 +25,7 @@
#include <wtsapi32.h>
#include <Winsvc.h>
#include <PowrProf.h>
+#include <signal.h>
// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
@@ -334,13 +335,15 @@ psutil_proc_kill(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
if (pid == 0)
- return AccessDenied();
+ return AccessDenied("");
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProcess == NULL) {
if (GetLastError() == ERROR_INVALID_PARAMETER) {
// see https://github.com/giampaolo/psutil/issues/24
- NoSuchProcess();
+ psutil_debug("OpenProcess -> ERROR_INVALID_PARAMETER turned "
+ "into NoSuchProcess");
+ NoSuchProcess("");
}
else {
PyErr_SetFromWindowsErr(0);
@@ -349,7 +352,7 @@ psutil_proc_kill(PyObject *self, PyObject *args) {
}
// kill the process
- if (! TerminateProcess(hProcess, 0)) {
+ if (! TerminateProcess(hProcess, SIGTERM)) {
err = GetLastError();
// See: https://github.com/giampaolo/psutil/issues/1099
if (err != ERROR_ACCESS_DENIED) {
@@ -378,7 +381,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "ll", &pid, &timeout))
return NULL;
if (pid == 0)
- return AccessDenied();
+ return AccessDenied("");
hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
FALSE, pid);
@@ -441,7 +444,7 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
// usually means the process has died so we throw a NoSuchProcess
// here
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
return PyErr_SetFromWindowsErr(0);
@@ -495,7 +498,7 @@ psutil_proc_create_time(PyObject *self, PyObject *args) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
// usually means the process has died so we throw a
// NoSuchProcess here
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
return PyErr_SetFromWindowsErr(0);
@@ -514,7 +517,7 @@ psutil_proc_create_time(PyObject *self, PyObject *args) {
CloseHandle(hProcess);
if (ret != 0) {
if (exitCode != STILL_ACTIVE)
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
// Ignore access denied as it means the process is still alive.
@@ -628,7 +631,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -651,7 +654,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -716,7 +719,7 @@ psutil_proc_name(PyObject *self, PyObject *args) {
}
CloseHandle(hSnapShot);
- NoSuchProcess();
+ NoSuchProcess("");
return NULL;
}
@@ -1037,7 +1040,6 @@ error:
/*
* Return process current working directory as a Python string.
*/
-
static PyObject *
psutil_proc_cwd(PyObject *self, PyObject *args) {
long pid;
@@ -1048,7 +1050,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -1063,10 +1065,11 @@ int
psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
// a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx
HANDLE hThreadSnap = NULL;
+ HANDLE hThread;
THREADENTRY32 te32 = {0};
if (pid == 0) {
- AccessDenied();
+ AccessDenied("");
return FALSE;
}
@@ -1088,20 +1091,17 @@ psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
// Walk the thread snapshot to find all threads of the process.
// If the thread belongs to the process, add its information
// to the display list.
- do
- {
- if (te32.th32OwnerProcessID == pid)
- {
- HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
- te32.th32ThreadID);
+ do {
+ if (te32.th32OwnerProcessID == pid) {
+ hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
+ te32.th32ThreadID);
if (hThread == NULL) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
CloseHandle(hThreadSnap);
return FALSE;
}
- if (suspend == 1)
- {
+ if (suspend == 1) {
if (SuspendThread(hThread) == (DWORD) - 1) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
@@ -1109,8 +1109,7 @@ psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
return FALSE;
}
}
- else
- {
+ else {
if (ResumeThread(hThread) == (DWORD) - 1) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
@@ -1172,13 +1171,13 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (pid == 0) {
// raise AD instead of returning 0 as procexp is able to
// retrieve useful information somehow
- AccessDenied();
+ AccessDenied("");
goto error;
}
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
if (pid_return == -1)
@@ -1569,7 +1568,7 @@ psutil_net_connections(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0) {
_psutil_conn_decref_objs();
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else if (pid_return == -1) {
_psutil_conn_decref_objs();
@@ -2303,8 +2302,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) {
py_nic_info = Py_BuildValue("(KKKKKKKK)",
pIfRow->OutOctets,
pIfRow->InOctets,
- pIfRow->OutUcastPkts,
- pIfRow->InUcastPkts,
+ (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts),
+ (pIfRow->InUcastPkts + pIfRow->InNUcastPkts),
pIfRow->InErrors,
pIfRow->OutErrors,
pIfRow->InDiscards,
@@ -2313,8 +2312,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) {
py_nic_info = Py_BuildValue("(kkkkkkkk)",
pIfRow->dwOutOctets,
pIfRow->dwInOctets,
- pIfRow->dwOutUcastPkts,
- pIfRow->dwInUcastPkts,
+ (pIfRow->dwOutUcastPkts + pIfRow->dwOutNUcastPkts),
+ (pIfRow->dwInUcastPkts + pIfRow->dwInNUcastPkts),
pIfRow->dwInErrors,
pIfRow->dwOutErrors,
pIfRow->dwInDiscards,
@@ -2365,6 +2364,9 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
char szDevice[MAX_PATH];
char szDeviceDisplay[MAX_PATH];
int devNum;
+ int i;
+ size_t ioctrlSize;
+ BOOL WINAPI ret;
PyObject *py_retdict = PyDict_New();
PyObject *py_tuple = NULL;
@@ -2379,38 +2381,74 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum);
hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
-
if (hDevice == INVALID_HANDLE_VALUE)
continue;
- if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
- &diskPerformance, sizeof(diskPerformance),
- &dwSize, NULL))
- {
- sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum);
- py_tuple = Py_BuildValue(
- "(IILLKK)",
- diskPerformance.ReadCount,
- diskPerformance.WriteCount,
- diskPerformance.BytesRead,
- diskPerformance.BytesWritten,
- (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000,
- (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000);
- if (!py_tuple)
- goto error;
- if (PyDict_SetItemString(py_retdict, szDeviceDisplay,
- py_tuple))
- {
- goto error;
+
+ // DeviceIoControl() sucks!
+ i = 0;
+ ioctrlSize = sizeof(diskPerformance);
+ while (1) {
+ i += 1;
+ ret = DeviceIoControl(
+ hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance,
+ ioctrlSize, &dwSize, NULL);
+ if (ret != 0)
+ break; // OK!
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ // Retry with a bigger buffer (+ limit for retries).
+ if (i <= 1024) {
+ ioctrlSize *= 2;
+ continue;
+ }
}
- Py_XDECREF(py_tuple);
- }
- else {
- // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when
- // compiling with mingw32; not sure what to do.
- // return PyErr_SetFromWindowsErr(0);
- ;;
+ else if (GetLastError() == ERROR_INVALID_FUNCTION) {
+ // This happens on AppVeyor:
+ // https://ci.appveyor.com/project/giampaolo/psutil/build/
+ // 1364/job/ascpdi271b06jle3
+ // Assume it means we're dealing with some exotic disk
+ // and go on.
+ psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; "
+ "ignore PhysicalDrive%i", devNum);
+ goto next;
+ }
+ else if (GetLastError() == ERROR_NOT_SUPPORTED) {
+ // Again, let's assume we're dealing with some exotic disk.
+ psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; "
+ "ignore PhysicalDrive%i", devNum);
+ goto next;
+ }
+ // XXX: it seems we should also catch ERROR_INVALID_PARAMETER:
+ // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/
+ // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/
+ // openafs-1.4.14/src/usd/usd_nt.c
+
+ // XXX: we can also bump into ERROR_MORE_DATA in which case
+ // (quoting doc) we're supposed to retry with a bigger buffer
+ // and specify a new "starting point", whatever it means.
+ PyErr_SetFromWindowsErr(0);
+ goto error;
}
+ sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum);
+ py_tuple = Py_BuildValue(
+ "(IILLKK)",
+ diskPerformance.ReadCount,
+ diskPerformance.WriteCount,
+ diskPerformance.BytesRead,
+ diskPerformance.BytesWritten,
+ // convert to ms:
+ // https://github.com/giampaolo/psutil/issues/1012
+ (unsigned long long)
+ (diskPerformance.ReadTime.QuadPart) / 10000000,
+ (unsigned long long)
+ (diskPerformance.WriteTime.QuadPart) / 10000000);
+ if (!py_tuple)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple))
+ goto error;
+ Py_XDECREF(py_tuple);
+
+next:
CloseHandle(hDevice);
}
@@ -2451,6 +2489,7 @@ static char *psutil_get_drive_type(int type) {
#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#endif
+
/*
* Return disk partitions as a list of tuples such as
* (drive_letter, drive_letter, type, "")
@@ -2460,11 +2499,15 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
DWORD num_bytes;
char drive_strings[255];
char *drive_letter = drive_strings;
+ char mp_buf[MAX_PATH];
+ char mp_path[MAX_PATH];
int all;
int type;
int ret;
unsigned int old_mode = 0;
char opts[20];
+ HANDLE mp_h;
+ BOOL mp_flag= TRUE;
LPTSTR fs_type[MAX_PATH + 1] = { 0 };
DWORD pflags = 0;
PyObject *py_all;
@@ -2535,6 +2578,40 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
strcat_s(opts, _countof(opts), "rw");
if (pflags & FILE_VOLUME_IS_COMPRESSED)
strcat_s(opts, _countof(opts), ",compressed");
+
+ // Check for mount points on this volume and add/get info
+ // (checks first to know if we can even have mount points)
+ if (pflags & FILE_SUPPORTS_REPARSE_POINTS) {
+
+ mp_h = FindFirstVolumeMountPoint(drive_letter, mp_buf, MAX_PATH);
+ if (mp_h != INVALID_HANDLE_VALUE) {
+ while (mp_flag) {
+
+ // Append full mount path with drive letter
+ strcpy_s(mp_path, _countof(mp_path), drive_letter);
+ strcat_s(mp_path, _countof(mp_path), mp_buf);
+
+ py_tuple = Py_BuildValue(
+ "(ssss)",
+ drive_letter,
+ mp_path,
+ fs_type, // Typically NTFS
+ opts);
+
+ if (!py_tuple || PyList_Append(py_retlist, py_tuple) == -1) {
+ FindVolumeMountPointClose(mp_h);
+ goto error;
+ }
+
+ Py_DECREF(py_tuple);
+
+ // Continue looking for more mount points
+ mp_flag = FindNextVolumeMountPoint(mp_h, mp_buf, MAX_PATH);
+ }
+ FindVolumeMountPointClose(mp_h);
+ }
+
+ }
}
if (strlen(opts) > 0)
@@ -3657,8 +3734,8 @@ PsutilMethods[] = {
"QueryDosDevice binding"},
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -3804,6 +3881,7 @@ void init_psutil_windows(void)
// set SeDebug for the current process
psutil_set_se_debug();
+ psutil_setup();
#if PY_MAJOR_VERSION >= 3
return module;
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 4584fd6e..505846e7 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -26,7 +26,8 @@ except ImportError as err:
# but if we get here it means this this was a wheel (or exe).
msg = "this Windows version is too old (< Windows Vista); "
msg += "psutil 3.4.2 is the latest version which supports Windows "
- msg += "2000, XP and 2003 server"
+ msg += "2000, XP and 2003 server; it may be possible that psutil "
+ msg += "will work if compiled from sources though"
raise RuntimeError(msg)
else:
raise
@@ -45,6 +46,9 @@ from ._compat import lru_cache
from ._compat import PY3
from ._compat import unicode
from ._compat import xrange
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import TimeoutExpired
from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
from ._psutil_windows import HIGH_PRIORITY_CLASS
@@ -139,11 +143,6 @@ pinfo_map = dict(
mem_private=21,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
diff --git a/psutil/arch/aix/common.c b/psutil/arch/aix/common.c
new file mode 100644
index 00000000..6115a15d
--- /dev/null
+++ b/psutil/arch/aix/common.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+#include <sys/core.h>
+#include <stdlib.h>
+#include "common.h"
+
+/* psutil_kread() - read from kernel memory */
+int
+psutil_kread(
+ int Kd, /* kernel memory file descriptor */
+ KA_T addr, /* kernel memory address */
+ char *buf, /* buffer to receive data */
+ size_t len) { /* length to read */
+ int br;
+
+ if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 1;
+ }
+ br = read(Kd, buf, len);
+ if (br == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 1;
+ }
+ if (br != len) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "size mismatch when reading kernel memory fd");
+ return 1;
+ }
+ return 0;
+}
+
+struct procentry64 *
+psutil_read_process_table(int * num) {
+ size_t msz;
+ pid32_t pid = 0;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ struct procentry64 *p;
+ int Np = 0; /* number of processes allocated in 'processes' */
+ int np = 0; /* number of processes read into 'processes' */
+ int i; /* number of processes read in current iteration */
+
+ msz = (size_t)(PROCSIZE * PROCINFO_INCR);
+ processes = (struct procentry64 *)malloc(msz);
+ if (!processes) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Np = PROCINFO_INCR;
+ p = processes;
+ while ((i = getprocs64(p, PROCSIZE, (struct fdsinfo64 *)NULL, 0, &pid,
+ PROCINFO_INCR))
+ == PROCINFO_INCR) {
+ np += PROCINFO_INCR;
+ if (np >= Np) {
+ msz = (size_t)(PROCSIZE * (Np + PROCINFO_INCR));
+ processes = (struct procentry64 *)realloc((char *)processes, msz);
+ if (!processes) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Np += PROCINFO_INCR;
+ }
+ p = (struct procentry64 *)((char *)processes + (np * PROCSIZE));
+ }
+
+ /* add the number of processes read in the last iteration */
+ if (i > 0)
+ np += i;
+
+ *num = np;
+ return processes;
+} \ No newline at end of file
diff --git a/psutil/arch/aix/common.h b/psutil/arch/aix/common.h
new file mode 100644
index 00000000..b677d8c2
--- /dev/null
+++ b/psutil/arch/aix/common.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __PSUTIL_AIX_COMMON_H__
+#define __PSUTIL_AIX_COMMON_H__
+
+#include <sys/core.h>
+
+#define PROCINFO_INCR (256)
+#define PROCSIZE (sizeof(struct procentry64))
+#define FDSINFOSIZE (sizeof(struct fdsinfo64))
+#define KMEM "/dev/kmem"
+
+typedef u_longlong_t KA_T;
+
+/* psutil_kread() - read from kernel memory */
+int psutil_kread(int Kd, /* kernel memory file descriptor */
+ KA_T addr, /* kernel memory address */
+ char *buf, /* buffer to receive data */
+ size_t len); /* length to read */
+
+struct procentry64 *
+psutil_read_process_table(
+ int * num /* out - number of processes read */
+);
+
+#endif /* __PSUTIL_AIX_COMMON_H__ */
diff --git a/psutil/arch/aix/ifaddrs.c b/psutil/arch/aix/ifaddrs.c
new file mode 100644
index 00000000..1a819365
--- /dev/null
+++ b/psutil/arch/aix/ifaddrs.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*! Based on code from
+ https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+!*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "ifaddrs.h"
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#define SIZE(p) MAX((p).sa_len,sizeof(p))
+
+
+static struct sockaddr *
+sa_dup(struct sockaddr *sa1)
+{
+ struct sockaddr *sa2;
+ size_t sz = sa1->sa_len;
+ sa2 = (struct sockaddr *) calloc(1, sz);
+ if (sa2 == NULL)
+ return NULL;
+ memcpy(sa2, sa1, sz);
+ return sa2;
+}
+
+
+void freeifaddrs(struct ifaddrs *ifp)
+{
+ if (NULL == ifp) return;
+ free(ifp->ifa_name);
+ free(ifp->ifa_addr);
+ free(ifp->ifa_netmask);
+ free(ifp->ifa_dstaddr);
+ freeifaddrs(ifp->ifa_next);
+ free(ifp);
+}
+
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+ int sd, ifsize;
+ char *ccp, *ecp;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct ifaddrs *cifa = NULL; /* current */
+ struct ifaddrs *pifa = NULL; /* previous */
+ const size_t IFREQSZ = sizeof(struct ifreq);
+ int fam;
+
+ *ifap = NULL;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd == -1)
+ goto error;
+
+ /* find how much memory to allocate for the SIOCGIFCONF call */
+ if (ioctl(sd, SIOCGSIZIFCONF, (caddr_t)&ifsize) < 0)
+ goto error;
+
+ ifc.ifc_req = (struct ifreq *) calloc(1, ifsize);
+ if (ifc.ifc_req == NULL)
+ goto error;
+ ifc.ifc_len = ifsize;
+
+ if (ioctl(sd, SIOCGIFCONF, &ifc) < 0)
+ goto error;
+
+ ccp = (char *)ifc.ifc_req;
+ ecp = ccp + ifsize;
+
+ while (ccp < ecp) {
+
+ ifr = (struct ifreq *) ccp;
+ ifsize = sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr);
+ fam = ifr->ifr_addr.sa_family;
+
+ if (fam == AF_INET || fam == AF_INET6) {
+ cifa = (struct ifaddrs *) calloc(1, sizeof(struct ifaddrs));
+ if (cifa == NULL)
+ goto error;
+ cifa->ifa_next = NULL;
+
+ if (pifa == NULL) *ifap = cifa; /* first one */
+ else pifa->ifa_next = cifa;
+
+ cifa->ifa_name = strdup(ifr->ifr_name);
+ if (cifa->ifa_name == NULL)
+ goto error;
+ cifa->ifa_flags = 0;
+ cifa->ifa_dstaddr = NULL;
+
+ cifa->ifa_addr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_addr == NULL)
+ goto error;
+
+ if (fam == AF_INET) {
+ if (ioctl(sd, SIOCGIFNETMASK, ifr, IFREQSZ) < 0)
+ goto error;
+ cifa->ifa_netmask = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_netmask == NULL)
+ goto error;
+ }
+
+ if (0 == ioctl(sd, SIOCGIFFLAGS, ifr)) /* optional */
+ cifa->ifa_flags = ifr->ifr_flags;
+
+ if (fam == AF_INET) {
+ if (ioctl(sd, SIOCGIFDSTADDR, ifr, IFREQSZ) < 0) {
+ if (0 == ioctl(sd, SIOCGIFBRDADDR, ifr, IFREQSZ)) {
+ cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_dstaddr == NULL)
+ goto error;
+ }
+ }
+ else {
+ cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_dstaddr == NULL)
+ goto error;
+ }
+ }
+ pifa = cifa;
+ }
+
+ ccp += ifsize;
+ }
+ free(ifc.ifc_req);
+ close(sd);
+ return 0;
+error:
+ if (ifc.ifc_req != NULL)
+ free(ifc.ifc_req);
+ if (sd != -1)
+ close(sd);
+ freeifaddrs(*ifap);
+ return (-1);
+} \ No newline at end of file
diff --git a/psutil/arch/aix/ifaddrs.h b/psutil/arch/aix/ifaddrs.h
new file mode 100644
index 00000000..3920c1cc
--- /dev/null
+++ b/psutil/arch/aix/ifaddrs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*! Based on code from
+ https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+!*/
+
+
+#ifndef GENERIC_AIX_IFADDRS_H
+#define GENERIC_AIX_IFADDRS_H
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#undef ifa_dstaddr
+#undef ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr
+
+struct ifaddrs {
+ struct ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_netmask;
+ struct sockaddr *ifa_dstaddr;
+};
+
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+
+#endif \ No newline at end of file
diff --git a/psutil/arch/aix/net_connections.c b/psutil/arch/aix/net_connections.c
new file mode 100644
index 00000000..69b43892
--- /dev/null
+++ b/psutil/arch/aix/net_connections.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Baded on code from lsof:
+ * http://www.ibm.com/developerworks/aix/library/au-lsof.html
+ * - dialects/aix/dproc.c:gather_proc_info
+ * - lib/prfp.c:process_file
+ * - dialects/aix/dsock.c:process_socket
+ * - dialects/aix/dproc.c:get_kernel_access
+*/
+
+#include <Python.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#define _KERNEL
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/types.h>
+#include <sys/core.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <netinet/in_pcb.h>
+#include <arpa/inet.h>
+
+#include "../../_psutil_common.h"
+#include "net_kernel_structs.h"
+#include "net_connections.h"
+#include "common.h"
+
+#define NO_SOCKET (PyObject *)(-1)
+
+static int
+read_unp_addr(
+ int Kd,
+ KA_T unp_addr,
+ char *buf,
+ size_t buflen
+) {
+ struct sockaddr_un *ua = (struct sockaddr_un *)NULL;
+ struct sockaddr_un un;
+ struct mbuf64 mb;
+ int uo;
+
+ if (psutil_kread(Kd, unp_addr, (char *)&mb, sizeof(mb))) {
+ return 1;
+ }
+
+ uo = (int)(mb.m_hdr.mh_data - unp_addr);
+ if ((uo + sizeof(struct sockaddr)) <= sizeof(mb))
+ ua = (struct sockaddr_un *)((char *)&mb + uo);
+ else {
+ if (psutil_kread(Kd, (KA_T)mb.m_hdr.mh_data,
+ (char *)&un, sizeof(un))) {
+ return 1;
+ }
+ ua = &un;
+ }
+ if (ua && ua->sun_path[0]) {
+ if (mb.m_len > sizeof(struct sockaddr_un))
+ mb.m_len = sizeof(struct sockaddr_un);
+ *((char *)ua + mb.m_len - 1) = '\0';
+ snprintf(buf, buflen, "%s", ua->sun_path);
+ }
+ return 0;
+}
+
+static PyObject *
+process_file(int Kd, pid32_t pid, int fd, KA_T fp) {
+ struct file64 f;
+ struct socket64 s;
+ struct protosw64 p;
+ struct domain d;
+ struct inpcb64 inp;
+ int fam;
+ struct tcpcb64 t;
+ int state = PSUTIL_CONN_NONE;
+ unsigned char *laddr = (unsigned char *)NULL;
+ unsigned char *raddr = (unsigned char *)NULL;
+ int rport, lport;
+ char laddr_str[INET6_ADDRSTRLEN];
+ char raddr_str[INET6_ADDRSTRLEN];
+ struct unpcb64 unp;
+ char unix_laddr_str[PATH_MAX] = { 0 };
+ char unix_raddr_str[PATH_MAX] = { 0 };
+
+ /* Read file structure */
+ if (psutil_kread(Kd, fp, (char *)&f, sizeof(f))) {
+ return NULL;
+ }
+ if (!f.f_count || f.f_type != DTYPE_SOCKET) {
+ return NO_SOCKET;
+ }
+
+ if (psutil_kread(Kd, (KA_T) f.f_data, (char *) &s, sizeof(s))) {
+ return NULL;
+ }
+
+ if (!s.so_type) {
+ return NO_SOCKET;
+ }
+
+ if (!s.so_proto) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol handle");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T)s.so_proto, (char *)&p, sizeof(p))) {
+ return NULL;
+ }
+
+ if (!p.pr_domain) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol domain");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T)p.pr_domain, (char *)&d, sizeof(d))) {
+ return NULL;
+ }
+
+ fam = d.dom_family;
+ if (fam == AF_INET || fam == AF_INET6) {
+ /* Read protocol control block */
+ if (!s.so_pcb) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket PCB");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *) &inp, sizeof(inp))) {
+ return NULL;
+ }
+
+ if (p.pr_protocol == IPPROTO_TCP) {
+ /* If this is a TCP socket, read its control block */
+ if (inp.inp_ppcb
+ && !psutil_kread(Kd, (KA_T)inp.inp_ppcb,
+ (char *)&t, sizeof(t)))
+ state = t.t_state;
+ }
+
+ if (fam == AF_INET6) {
+ laddr = (unsigned char *)&inp.inp_laddr6;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp.inp_faddr6)) {
+ raddr = (unsigned char *)&inp.inp_faddr6;
+ rport = (int)ntohs(inp.inp_fport);
+ }
+ }
+ if (fam == AF_INET) {
+ laddr = (unsigned char *)&inp.inp_laddr;
+ if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport != 0) {
+ raddr = (unsigned char *)&inp.inp_faddr;
+ rport = (int)ntohs(inp.inp_fport);
+ }
+ }
+ lport = (int)ntohs(inp.inp_lport);
+
+ inet_ntop(fam, laddr, laddr_str, sizeof(laddr_str));
+
+ if (raddr != NULL) {
+ inet_ntop(fam, raddr, raddr_str, sizeof(raddr_str));
+ return Py_BuildValue("(iii(si)(si)ii)", fd, fam,
+ s.so_type, laddr_str, lport, raddr_str,
+ rport, state, pid);
+ }
+ else {
+ return Py_BuildValue("(iii(si)()ii)", fd, fam,
+ s.so_type, laddr_str, lport, state,
+ pid);
+ }
+ }
+
+
+ if (fam == AF_UNIX) {
+ if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *)&unp, sizeof(unp))) {
+ return NULL;
+ }
+ if ((KA_T) f.f_data != (KA_T) unp.unp_socket) {
+ PyErr_SetString(PyExc_RuntimeError, "unp_socket mismatch");
+ return NULL;
+ }
+
+ if (unp.unp_addr) {
+ if (read_unp_addr(Kd, unp.unp_addr, unix_laddr_str,
+ sizeof(unix_laddr_str))) {
+ return NULL;
+ }
+ }
+
+ if (unp.unp_conn) {
+ if (psutil_kread(Kd, (KA_T) unp.unp_conn, (char *)&unp,
+ sizeof(unp))) {
+ return NULL;
+ }
+ if (read_unp_addr(Kd, unp.unp_addr, unix_raddr_str,
+ sizeof(unix_raddr_str))) {
+ return NULL;
+ }
+ }
+
+ return Py_BuildValue("(iiissii)", fd, d.dom_family,
+ s.so_type, unix_laddr_str, unix_raddr_str, PSUTIL_CONN_NONE,
+ pid);
+ }
+ return NO_SOCKET;
+}
+
+PyObject *
+psutil_net_connections(PyObject *self, PyObject *args) {
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ KA_T fp;
+ int Kd = -1;
+ int i, np;
+ struct procentry64 *p;
+ struct fdsinfo64 *fds = (struct fdsinfo64 *)NULL;
+ pid32_t requested_pid;
+ pid32_t pid;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ /* the process table */
+
+ if (py_retlist == NULL)
+ goto error;
+ if (! PyArg_ParseTuple(args, "i", &requested_pid))
+ goto error;
+
+ Kd = open(KMEM, O_RDONLY, 0);
+ if (Kd < 0) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, KMEM);
+ goto error;
+ }
+
+ processes = psutil_read_process_table(&np);
+ if (!processes)
+ goto error;
+
+ /* Loop through processes */
+ for (p = processes; np > 0; np--, p++) {
+ pid = p->pi_pid;
+ if (requested_pid != -1 && requested_pid != pid)
+ continue;
+ if (p->pi_state == 0 || p->pi_state == SZOMB)
+ continue;
+
+ if (!fds) {
+ fds = (struct fdsinfo64 *)malloc((size_t)FDSINFOSIZE);
+ if (!fds) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+ if (getprocs64((struct procentry64 *)NULL, PROCSIZE, fds, FDSINFOSIZE,
+ &pid, 1)
+ != 1)
+ continue;
+
+ /* loop over file descriptors */
+ for (i = 0; i < p->pi_maxofile; i++) {
+ fp = (KA_T)fds->pi_ufd[i].fp;
+ if (fp) {
+ py_tuple = process_file(Kd, p->pi_pid, i, fp);
+ if (py_tuple == NULL)
+ goto error;
+ if (py_tuple != NO_SOCKET) {
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ }
+ }
+ }
+ close(Kd);
+ free(processes);
+ if (fds != NULL)
+ free(fds);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (Kd > 0)
+ close(Kd);
+ if (processes != NULL)
+ free(processes);
+ if (fds != NULL)
+ free(fds);
+ return NULL;
+}
diff --git a/psutil/arch/aix/net_connections.h b/psutil/arch/aix/net_connections.h
new file mode 100644
index 00000000..222bcaf3
--- /dev/null
+++ b/psutil/arch/aix/net_connections.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __NET_CONNECTIONS_H__
+#define __NET_CONNECTIONS_H__
+
+#include <Python.h>
+
+PyObject* psutil_net_connections(PyObject *self, PyObject *args);
+
+#endif /* __NET_CONNECTIONS_H__ */ \ No newline at end of file
diff --git a/psutil/arch/aix/net_kernel_structs.h b/psutil/arch/aix/net_kernel_structs.h
new file mode 100644
index 00000000..09f320ff
--- /dev/null
+++ b/psutil/arch/aix/net_kernel_structs.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* The kernel is always 64 bit but Python is usually compiled as a 32 bit
+ * process. We're reading the kernel memory to get the network connections,
+ * so we need the structs we read to be defined with 64 bit "pointers".
+ * Here are the partial definitions of the structs we use, taken from the
+ * header files, with data type sizes converted to their 64 bit counterparts,
+ * and unused data truncated. */
+
+#ifdef __64BIT__
+/* In case we're in a 64 bit process after all */
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/unpcb.h>
+#include <sys/mbuf_base.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#define file64 file
+#define socket64 socket
+#define protosw64 protosw
+#define inpcb64 inpcb
+#define tcpcb64 tcpcb
+#define unpcb64 unpcb
+#define mbuf64 mbuf
+#else
+ struct file64 {
+ int f_flag;
+ int f_count;
+ int f_options;
+ int f_type;
+ u_longlong_t f_data;
+ };
+
+ struct socket64 {
+ short so_type; /* generic type, see socket.h */
+ short so_options; /* from socket call, see socket.h */
+ ushort so_linger; /* time to linger while closing */
+ short so_state; /* internal state flags SS_*, below */
+ u_longlong_t so_pcb; /* protocol control block */
+ u_longlong_t so_proto; /* protocol handle */
+ };
+
+ struct protosw64 {
+ short pr_type; /* socket type used for */
+ u_longlong_t pr_domain; /* domain protocol a member of */
+ short pr_protocol; /* protocol number */
+ short pr_flags; /* see below */
+ };
+
+ struct inpcb64 {
+ u_longlong_t inp_next,inp_prev;
+ /* pointers to other pcb's */
+ u_longlong_t inp_head; /* pointer back to chain of inpcb's
+ for this protocol */
+ u_int32_t inp_iflowinfo; /* input flow label */
+ u_short inp_fport; /* foreign port */
+ u_int16_t inp_fatype; /* foreign address type */
+ union in_addr_6 inp_faddr_6; /* foreign host table entry */
+ u_int32_t inp_oflowinfo; /* output flow label */
+ u_short inp_lport; /* local port */
+ u_int16_t inp_latype; /* local address type */
+ union in_addr_6 inp_laddr_6; /* local host table entry */
+ u_longlong_t inp_socket; /* back pointer to socket */
+ u_longlong_t inp_ppcb; /* pointer to per-protocol pcb */
+ u_longlong_t space_rt;
+ struct sockaddr_in6 spare_dst;
+ u_longlong_t inp_ifa; /* interface address to use */
+ int inp_flags; /* generic IP/datagram flags */
+};
+
+struct tcpcb64 {
+ u_longlong_t seg__next;
+ u_longlong_t seg__prev;
+ short t_state; /* state of this connection */
+};
+
+struct unpcb64 {
+ u_longlong_t unp_socket; /* pointer back to socket */
+ u_longlong_t unp_vnode; /* if associated with file */
+ ino_t unp_vno; /* fake vnode number */
+ u_longlong_t unp_conn; /* control block of connected socket */
+ u_longlong_t unp_refs; /* referencing socket linked list */
+ u_longlong_t unp_nextref; /* link in unp_refs list */
+ u_longlong_t unp_addr; /* bound address of socket */
+};
+
+struct m_hdr64
+{
+ u_longlong_t mh_next; /* next buffer in chain */
+ u_longlong_t mh_nextpkt; /* next chain in queue/record */
+ long mh_len; /* amount of data in this mbuf */
+ u_longlong_t mh_data; /* location of data */
+};
+
+struct mbuf64
+{
+ struct m_hdr64 m_hdr;
+};
+
+#define m_len m_hdr.mh_len
+
+#endif \ No newline at end of file
diff --git a/psutil/arch/freebsd/proc_socks.c b/psutil/arch/freebsd/proc_socks.c
index 9b03e059..a458a01e 100644
--- a/psutil/arch/freebsd/proc_socks.c
+++ b/psutil/arch/freebsd/proc_socks.c
@@ -136,20 +136,36 @@ psutil_search_tcplist(char *buf, struct kinfo_file *kif) {
if (kif->kf_sock_domain == AF_INET) {
if (!psutil_sockaddr_matches(
AF_INET, inp->inp_lport, &inp->inp_laddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local))
+#else
+ &kif->kf_un.kf_sock.kf_sa_local))
+#endif
continue;
if (!psutil_sockaddr_matches(
AF_INET, inp->inp_fport, &inp->inp_faddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer))
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
continue;
} else {
if (!psutil_sockaddr_matches(
AF_INET6, inp->inp_lport, &inp->in6p_laddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local))
+#else
+ &kif->kf_un.kf_sock.kf_sa_local))
+#endif
continue;
if (!psutil_sockaddr_matches(
AF_INET6, inp->inp_fport, &inp->in6p_faddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer))
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
continue;
}
@@ -196,7 +212,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -243,19 +259,35 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
inet_ntop(
kif->kf_sock_domain,
psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local),
+#else
+ &kif->kf_un.kf_sock.kf_sa_local),
+#endif
lip,
sizeof(lip));
inet_ntop(
kif->kf_sock_domain,
psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer),
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer),
+#endif
rip,
sizeof(rip));
lport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local));
+#else
+ &kif->kf_un.kf_sock.kf_sa_local));
+#endif
rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer));
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer));
+#endif
// construct python tuple/list
py_laddr = Py_BuildValue("(si)", lip, lport);
@@ -287,7 +319,11 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
else if (kif->kf_sock_domain == AF_UNIX) {
struct sockaddr_un *sun;
+#if __FreeBSD_version < 1200031
sun = (struct sockaddr_un *)&kif->kf_sa_local;
+#else
+ sun = (struct sockaddr_un *)&kif->kf_un.kf_sock.kf_sa_local;
+#endif
snprintf(
path, sizeof(path), "%.*s",
(int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c
index 006c813c..52e2ae4a 100644
--- a/psutil/arch/freebsd/specific.c
+++ b/psutil/arch/freebsd/specific.c
@@ -59,7 +59,7 @@ psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) {
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -297,7 +297,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
if (ret == -1)
return NULL;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else
strcpy(pathname, "");
}
@@ -354,7 +354,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -370,7 +370,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -548,7 +548,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -597,7 +597,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
@@ -765,7 +765,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getvmmap(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getvmmap() failed");
+ psutil_raise_for_pid(pid, "kinfo_getvmmap()");
goto error;
}
for (i = 0; i < cnt; i++) {
diff --git a/psutil/arch/netbsd/specific.c b/psutil/arch/netbsd/specific.c
index 1dc2080e..cab60d60 100644
--- a/psutil/arch/netbsd/specific.c
+++ b/psutil/arch/netbsd/specific.c
@@ -72,7 +72,7 @@ psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) {
}
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -157,7 +157,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
if (ret == -1)
return NULL;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else
strcpy(pathname, "");
}
@@ -209,7 +209,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -226,7 +226,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -502,7 +502,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
diff --git a/psutil/arch/openbsd/specific.c b/psutil/arch/openbsd/specific.c
index de30c4d7..33ebdeec 100644
--- a/psutil/arch/openbsd/specific.c
+++ b/psutil/arch/openbsd/specific.c
@@ -67,7 +67,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
}
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -242,7 +242,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
if (! kd) {
if (strstr(errbuf, "Permission denied") != NULL)
- AccessDenied();
+ AccessDenied("");
else
PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() syscall failed");
goto error;
@@ -253,7 +253,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
sizeof(*kp), &nentries);
if (! kp) {
if (strstr(errbuf, "Permission denied") != NULL)
- AccessDenied();
+ AccessDenied("");
else
PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed");
goto error;
@@ -404,7 +404,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
@@ -509,7 +509,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
diff --git a/psutil/arch/osx/process_info.c b/psutil/arch/osx/process_info.c
index 7c715be8..40c79a2c 100644
--- a/psutil/arch/osx/process_info.c
+++ b/psutil/arch/osx/process_info.c
@@ -144,7 +144,7 @@ psutil_get_cmdline(long pid) {
// In case of zombie process we'll get EINVAL. We translate it
// to NSP and _psosx.py will translate it to ZP.
if ((errno == EINVAL) && (psutil_pid_exists(pid)))
- NoSuchProcess();
+ NoSuchProcess("");
else
PyErr_SetFromErrno(PyExc_OSError);
goto error;
@@ -238,7 +238,7 @@ psutil_get_environ(long pid) {
// In case of zombie process we'll get EINVAL. We translate it
// to NSP and _psosx.py will translate it to ZP.
if ((errno == EINVAL) && (psutil_pid_exists(pid)))
- NoSuchProcess();
+ NoSuchProcess("");
else
PyErr_SetFromErrno(PyExc_OSError);
goto error;
@@ -338,7 +338,7 @@ psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) {
// sysctl succeeds but len is zero, happens when process has gone away
if (len == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -354,7 +354,7 @@ 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");
+ psutil_raise_for_pid(pid, "proc_pidinfo()");
return 0;
}
return ret;
diff --git a/psutil/arch/solaris/v10/ifaddrs.h b/psutil/arch/solaris/v10/ifaddrs.h
index d2771193..0953a9b9 100644
--- a/psutil/arch/solaris/v10/ifaddrs.h
+++ b/psutil/arch/solaris/v10/ifaddrs.h
@@ -1,8 +1,8 @@
/* Reference: https://lists.samba.org/archive/samba-technical/2009-February/063079.html */
-#ifndef __IFADDRS_H___
-#define __IFADDRS_H___
+#ifndef __IFADDRS_H__
+#define __IFADDRS_H__
#include <sys/socket.h>
#include <net/if.h>
diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h
index 1bbbf2ac..ea23ddb7 100644
--- a/psutil/arch/windows/ntextapi.h
+++ b/psutil/arch/windows/ntextapi.h
@@ -163,13 +163,15 @@ typedef enum _KWAIT_REASON {
} KWAIT_REASON, *PKWAIT_REASON;
-typedef struct _CLIENT_ID {
+typedef struct _CLIENT_ID2 {
HANDLE UniqueProcess;
HANDLE UniqueThread;
-} CLIENT_ID, *PCLIENT_ID;
+} CLIENT_ID2, *PCLIENT_ID2;
+#define CLIENT_ID CLIENT_ID2
+#define PCLIENT_ID PCLIENT_ID2
-typedef struct _SYSTEM_THREAD_INFORMATION {
+typedef struct _SYSTEM_THREAD_INFORMATION2 {
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
@@ -181,8 +183,10 @@ typedef struct _SYSTEM_THREAD_INFORMATION {
ULONG ContextSwitches;
ULONG ThreadState;
KWAIT_REASON WaitReason;
-} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
+} SYSTEM_THREAD_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;
+#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2
+#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2
typedef struct _TEB *PTEB;
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index a9687f9c..ffd3c80e 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -256,7 +256,7 @@ psutil_check_phandle(HANDLE hProcess, DWORD pid) {
if (ret == 1)
return hProcess;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else if (ret == -1)
return PyErr_SetFromWindowsErr(0);
else // -2
@@ -277,7 +277,7 @@ psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) {
if (pid == 0) {
// otherwise we'd get NoSuchProcess
- return AccessDenied();
+ return AccessDenied("");
}
hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
@@ -337,7 +337,7 @@ psutil_get_pids(DWORD *numberOfReturnedPIDs) {
int
psutil_assert_pid_exists(DWORD pid, char *err) {
- if (psutil_testing()) {
+ if (PSUTIL_TESTING) {
if (psutil_pid_in_pids(pid) == 0) {
PyErr_SetString(PyExc_AssertionError, err);
return 0;
@@ -349,7 +349,7 @@ psutil_assert_pid_exists(DWORD pid, char *err) {
int
psutil_assert_pid_not_exists(DWORD pid, char *err) {
- if (psutil_testing()) {
+ if (PSUTIL_TESTING) {
if (psutil_pid_in_pids(pid) == 1) {
PyErr_SetString(PyExc_AssertionError, err);
return 0;
@@ -959,7 +959,7 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
}
} while ( (process = PSUTIL_NEXT_PROCESS(process)) );
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
error:
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 7d8e5def..0890b6f9 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -65,7 +65,7 @@ else:
__all__ = [
# constants
'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'MEMORY_TOLERANCE', 'NO_RETRIES',
- 'PYPY', 'PYTHON', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX',
+ 'PYPY', 'PYTHON_EXE', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX',
'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'VALID_PROC_STATUSES',
'VERBOSITY',
"HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS",
@@ -159,6 +159,7 @@ HAS_MEMORY_FULL_INFO = 'uss' in psutil.Process().memory_full_info()._fields
HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps")
HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num")
HAS_RLIMIT = hasattr(psutil.Process, "rlimit")
+HAS_THREADS = hasattr(psutil.Process, "threads")
HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery")
HAS_BATTERY = HAS_SENSORS_BATTERY and psutil.sensors_battery()
HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans")
@@ -166,7 +167,33 @@ HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures")
# --- misc
-PYTHON = os.path.realpath(sys.executable)
+
+def _get_py_exe():
+ def attempt(exe):
+ try:
+ subprocess.check_call(
+ [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except Exception:
+ return None
+ else:
+ return exe
+
+ if OSX:
+ exe = \
+ attempt(sys.executable) or \
+ attempt(os.path.realpath(sys.executable)) or \
+ attempt(which("python%s.%s" % sys.version_info[:2])) or \
+ attempt(psutil.Process().exe())
+ if not exe:
+ raise ValueError("can't find python exe real abspath")
+ return exe
+ else:
+ exe = os.path.realpath(sys.executable)
+ assert os.path.exists(exe), exe
+ return exe
+
+
+PYTHON_EXE = _get_py_exe()
DEVNULL = open(os.devnull, 'r+')
VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil)
if x.startswith('STATUS_')]
@@ -182,7 +209,11 @@ _testfiles_created = set()
def _cleanup_files():
DEVNULL.close()
for name in os.listdir(u('.')):
- if name.startswith(u(TESTFILE_PREFIX)):
+ if isinstance(name, unicode):
+ prefix = u(TESTFILE_PREFIX)
+ else:
+ prefix = TESTFILE_PREFIX
+ if name.startswith(prefix):
try:
safe_rmpath(name)
except Exception:
@@ -286,7 +317,7 @@ def get_test_subprocess(cmd=None, **kwds):
pyline = "from time import sleep;" \
"open(r'%s', 'w').close();" \
"sleep(60);" % _TESTFN
- cmd = [PYTHON, "-c", pyline]
+ cmd = [PYTHON_EXE, "-c", pyline]
sproc = subprocess.Popen(cmd, **kwds)
_subprocesses_started.add(sproc)
wait_for_file(_TESTFN, delete=True, empty=True)
@@ -308,15 +339,14 @@ def create_proc_children_pair():
_TESTFN2 = os.path.basename(_TESTFN) + '2' # need to be relative
s = textwrap.dedent("""\
import subprocess, os, sys, time
- PYTHON = os.path.realpath(sys.executable)
s = "import os, time;"
s += "f = open('%s', 'w');"
s += "f.write(str(os.getpid()));"
s += "f.close();"
s += "time.sleep(60);"
- subprocess.Popen([PYTHON, '-c', s])
+ subprocess.Popen(['%s', '-c', s])
time.sleep(60)
- """ % _TESTFN2)
+ """ % (_TESTFN2, PYTHON_EXE))
# On Windows if we create a subprocess with CREATE_NO_WINDOW flag
# set (which is the default) a "conhost.exe" extra process will be
# spawned as a child. We don't want that.
@@ -382,7 +412,7 @@ def pyrun(src, **kwds):
_testfiles_created.add(f.name)
f.write(src)
f.flush()
- subp = get_test_subprocess([PYTHON, f.name], **kwds)
+ subp = get_test_subprocess([PYTHON_EXE, f.name], **kwds)
wait_for_pid(subp.pid)
return subp
@@ -689,7 +719,7 @@ def create_exe(outpath, c_code=None):
if c_code:
if not which("gcc"):
raise ValueError("gcc is not installed")
- if c_code is None:
+ if isinstance(c_code, bool): # c_code is True
c_code = textwrap.dedent(
"""
#include <unistd.h>
@@ -698,6 +728,7 @@ def create_exe(outpath, c_code=None):
return 1;
}
""")
+ assert isinstance(c_code, str), c_code
with tempfile.NamedTemporaryFile(
suffix='.c', delete=False, mode='wt') as f:
f.write(c_code)
@@ -707,7 +738,7 @@ def create_exe(outpath, c_code=None):
safe_rmpath(f.name)
else:
# copy python executable
- shutil.copyfile(sys.executable, outpath)
+ shutil.copyfile(PYTHON_EXE, outpath)
if POSIX:
st = os.stat(outpath)
os.chmod(outpath, st.st_mode | stat.S_IEXEC)
@@ -742,16 +773,21 @@ unittest.TestCase = TestCase
def _setup_tests():
- assert 'PSUTIL_TESTING' in os.environ
- assert psutil._psplatform.cext.py_psutil_testing()
+ if 'PSUTIL_TESTING' not in os.environ:
+ # This won't work on Windows but set_testing() below will do it.
+ os.environ['PSUTIL_TESTING'] = '1'
+ psutil._psplatform.cext.set_testing()
def get_suite():
- testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
- if x.endswith('.py') and x.startswith('test_') and not
- x.startswith('test_memory_leaks')]
+ testmods = [os.path.splitext(x)[0] for x in os.listdir(HERE)
+ if x.endswith('.py') and x.startswith('test_') and not
+ x.startswith('test_memory_leaks')]
+ if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ:
+ testmods = [x for x in testmods if not x.endswith((
+ "osx", "posix", "linux"))]
suite = unittest.TestSuite()
- for tm in testmodules:
+ for tm in testmods:
# ...so that the full test paths are printed on screen
tm = "psutil.tests.%s" % tm
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
diff --git a/psutil/tests/__main__.py b/psutil/tests/__main__.py
index 475e6b81..2cdf5c42 100755
--- a/psutil/tests/__main__.py
+++ b/psutil/tests/__main__.py
@@ -21,11 +21,11 @@ try:
except ImportError:
from urllib2 import urlopen
+from psutil.tests import PYTHON_EXE
from psutil.tests import run_suite
HERE = os.path.abspath(os.path.dirname(__file__))
-PYTHON = os.path.basename(sys.executable)
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
TEST_DEPS = []
if sys.version_info[:2] == (2, 6):
@@ -54,7 +54,7 @@ def install_pip():
f.flush()
print("installing pip")
- code = os.system('%s %s --user' % (sys.executable, f.name))
+ code = os.system('%s %s --user' % (PYTHON_EXE, f.name))
return code
@@ -68,12 +68,12 @@ def install_test_deps(deps=None):
opts = "--user" if not is_venv else ""
install_pip()
code = os.system('%s -m pip install %s --upgrade %s' % (
- sys.executable, opts, " ".join(deps)))
+ PYTHON_EXE, opts, " ".join(deps)))
return code
def main():
- usage = "%s -m psutil.tests [opts]" % PYTHON
+ usage = "%s -m psutil.tests [opts]" % PYTHON_EXE
parser = optparse.OptionParser(usage=usage, description="run unit tests")
parser.add_option("-i", "--install-deps",
action="store_true", default=False,
@@ -88,8 +88,8 @@ def main():
try:
__import__(dep.split("==")[0])
except ImportError:
- sys.exit("%r lib is not installed; run:\n"
- "%s -m psutil.tests --install-deps" % (dep, PYTHON))
+ sys.exit("%r lib is not installed; run %s -m psutil.tests "
+ "--install-deps" % (dep, PYTHON_EXE))
run_suite()
diff --git a/psutil/tests/test_aix.py b/psutil/tests/test_aix.py
new file mode 100755
index 00000000..7a8a4c33
--- /dev/null
+++ b/psutil/tests/test_aix.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola'
+# Copyright (c) 2017, Arnon Yaari
+# All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""AIX specific tests."""
+
+import re
+
+from psutil import AIX
+from psutil.tests import run_test_module_by_name
+from psutil.tests import sh
+from psutil.tests import unittest
+import psutil
+
+
+@unittest.skipIf(not AIX, "AIX only")
+class AIXSpecificTestCase(unittest.TestCase):
+
+ def test_virtual_memory(self):
+ out = sh('/usr/bin/svmon -O unit=KB')
+ re_pattern = "memory\s*"
+ for field in ("size inuse free pin virtual available mmode").split():
+ re_pattern += "(?P<%s>\S+)\s+" % (field,)
+ matchobj = re.search(re_pattern, out)
+
+ self.assertIsNotNone(
+ matchobj, "svmon command returned unexpected output")
+
+ KB = 1024
+ total = int(matchobj.group("size")) * KB
+ available = int(matchobj.group("available")) * KB
+ used = int(matchobj.group("inuse")) * KB
+ free = int(matchobj.group("free")) * KB
+
+ psutil_result = psutil.virtual_memory()
+
+ # MEMORY_TOLERANCE from psutil.tests is not enough. For some reason
+ # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance
+ # when compared to GBs.
+ MEMORY_TOLERANCE = 2 * KB * KB # 2 MB
+ self.assertEqual(psutil_result.total, total)
+ self.assertAlmostEqual(
+ psutil_result.used, used, delta=MEMORY_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.available, available, delta=MEMORY_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.free, free, delta=MEMORY_TOLERANCE)
+
+ def test_swap_memory(self):
+ out = sh('/usr/sbin/lsps -a')
+ # From the man page, "The size is given in megabytes" so we assume
+ # we'll always have 'MB' in the result
+ # TODO maybe try to use "swap -l" to check "used" too, but its units
+ # are not guaranteed to be "MB" so parsing may not be consistent
+ matchobj = re.search("(?P<space>\S+)\s+"
+ "(?P<vol>\S+)\s+"
+ "(?P<vg>\S+)\s+"
+ "(?P<size>\d+)MB", out)
+
+ self.assertIsNotNone(
+ matchobj, "lsps command returned unexpected output")
+
+ total_mb = int(matchobj.group("size"))
+ MB = 1024 ** 2
+ psutil_result = psutil.swap_memory()
+ # we divide our result by MB instead of multiplying the lsps value by
+ # MB because lsps may round down, so we round down too
+ self.assertEqual(int(psutil_result.total / MB), total_mb)
+
+ def test_cpu_stats(self):
+ out = sh('/usr/bin/mpstat -a')
+
+ re_pattern = "ALL\s*"
+ for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq "
+ "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd "
+ "sysc").split():
+ re_pattern += "(?P<%s>\S+)\s+" % (field,)
+ matchobj = re.search(re_pattern, out)
+
+ self.assertIsNotNone(
+ matchobj, "mpstat command returned unexpected output")
+
+ # numbers are usually in the millions so 1000 is ok for tolerance
+ CPU_STATS_TOLERANCE = 1000
+ psutil_result = psutil.cpu_stats()
+ self.assertAlmostEqual(
+ psutil_result.ctx_switches,
+ int(matchobj.group("cs")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.syscalls,
+ int(matchobj.group("sysc")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.interrupts,
+ int(matchobj.group("dev")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.soft_interrupts,
+ int(matchobj.group("soft")),
+ delta=CPU_STATS_TOLERANCE)
+
+ def test_cpu_count_logical(self):
+ out = sh('/usr/bin/mpstat -a')
+ mpstat_lcpu = int(re.search("lcpu=(\d+)", out).group(1))
+ psutil_lcpu = psutil.cpu_count(logical=True)
+ self.assertEqual(mpstat_lcpu, psutil_lcpu)
+
+ def test_net_if_addrs_names(self):
+ out = sh('/etc/ifconfig -l')
+ ifconfig_names = set(out.split())
+ psutil_names = set(psutil.net_if_addrs().keys())
+ self.assertSetEqual(ifconfig_names, psutil_names)
+
+
+if __name__ == '__main__':
+ run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py
index 203ddebb..176e2664 100755
--- a/psutil/tests/test_connections.py
+++ b/psutil/tests/test_connections.py
@@ -152,6 +152,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase):
assert not conn.raddr
self.assertEqual(conn.status, psutil.CONN_LISTEN)
+ @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
def test_tcp_v6(self):
addr = ("::1", get_free_port())
with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock:
@@ -166,6 +167,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase):
assert not conn.raddr
self.assertEqual(conn.status, psutil.CONN_NONE)
+ @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
def test_udp_v6(self):
addr = ("::1", get_free_port())
with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock:
@@ -418,7 +420,7 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
# =====================================================================
-class TestSystemWideConnections(unittest.TestCase):
+class TestSystemWideConnections(Base, unittest.TestCase):
"""Tests for net_connections()."""
@skip_on_access_denied()
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index 95bf2146..f9543e57 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -14,8 +14,10 @@ import os
import stat
import time
import traceback
+import warnings
from contextlib import closing
+from psutil import AIX
from psutil import BSD
from psutil import FREEBSD
from psutil import LINUX
@@ -65,7 +67,8 @@ class TestAvailability(unittest.TestCase):
self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS)
def test_PROCFS_PATH(self):
- self.assertEqual(hasattr(psutil, "PROCFS_PATH"), LINUX or SUNOS)
+ self.assertEqual(hasattr(psutil, "PROCFS_PATH"),
+ LINUX or SUNOS or AIX)
def test_win_priority(self):
ae = self.assertEqual
@@ -108,7 +111,10 @@ class TestAvailability(unittest.TestCase):
ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit)
def test_cpu_freq(self):
- self.assertEqual(hasattr(psutil, "cpu_freq"), LINUX or OSX or WINDOWS)
+ linux = (LINUX and
+ (os.path.exists("/sys/devices/system/cpu/cpufreq") or
+ os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq")))
+ self.assertEqual(hasattr(psutil, "cpu_freq"), linux or OSX or WINDOWS)
def test_sensors_temperatures(self):
self.assertEqual(hasattr(psutil, "sensors_temperatures"), LINUX)
@@ -118,7 +124,7 @@ class TestAvailability(unittest.TestCase):
def test_battery(self):
self.assertEqual(hasattr(psutil, "sensors_battery"),
- LINUX or WINDOWS or FREEBSD)
+ LINUX or WINDOWS or FREEBSD or OSX)
def test_proc_environ(self):
self.assertEqual(hasattr(psutil.Process, "environ"),
@@ -159,7 +165,23 @@ class TestAvailability(unittest.TestCase):
def test_proc_memory_maps(self):
hasit = hasattr(psutil.Process, "memory_maps")
- self.assertEqual(hasit, False if OPENBSD or NETBSD else True)
+ self.assertEqual(hasit, False if OPENBSD or NETBSD or AIX else True)
+
+
+# ===================================================================
+# --- Test deprecations
+# ===================================================================
+
+
+class TestDeprecations(unittest.TestCase):
+
+ def test_memory_info_ex(self):
+ with warnings.catch_warnings(record=True) as ws:
+ psutil.Process().memory_info_ex()
+ w = ws[0]
+ self.assertIsInstance(w.category(), FutureWarning)
+ self.assertIn("memory_info_ex() is deprecated", str(w.message))
+ self.assertIn("use memory_info() instead", str(w.message))
# ===================================================================
@@ -372,12 +394,14 @@ class TestFetchAllProcesses(unittest.TestCase):
self.assertGreaterEqual(ret, 0)
def ppid(self, ret, proc):
- self.assertIsInstance(ret, int)
+ self.assertIsInstance(ret, (int, long))
self.assertGreaterEqual(ret, 0)
def name(self, ret, proc):
self.assertIsInstance(ret, str)
- assert ret
+ # on AIX, "<exiting>" processes don't have names
+ if not AIX:
+ assert ret
def create_time(self, ret, proc):
self.assertIsInstance(ret, float)
@@ -482,7 +506,7 @@ class TestFetchAllProcesses(unittest.TestCase):
for value in ret:
self.assertIsInstance(value, (int, long))
self.assertGreaterEqual(value, 0)
- if POSIX and ret.vms != 0:
+ if POSIX and not AIX and ret.vms != 0:
# VMS is always supposed to be the highest
for name in ret._fields:
if name != 'vms':
@@ -536,8 +560,8 @@ class TestFetchAllProcesses(unittest.TestCase):
check_connection_ntuple(conn)
def cwd(self, ret, proc):
- self.assertIsInstance(ret, str)
- if ret is not None: # BSD may return None
+ if ret: # 'ret' can be None or empty
+ self.assertIsInstance(ret, str)
assert os.path.isabs(ret), ret
try:
st = os.stat(ret)
@@ -607,7 +631,7 @@ class TestFetchAllProcesses(unittest.TestCase):
def num_ctx_switches(self, ret, proc):
assert is_namedtuple(ret)
for value in ret:
- self.assertIsInstance(value, int)
+ self.assertIsInstance(value, (int, long))
self.assertGreaterEqual(value, 0)
def rlimit(self, ret, proc):
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 468b3c66..6ba17b25 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -29,6 +29,7 @@ from psutil._compat import PY3
from psutil._compat import u
from psutil.tests import call_until
from psutil.tests import HAS_BATTERY
+from psutil.tests import HAS_CPU_FREQ
from psutil.tests import HAS_RLIMIT
from psutil.tests import MEMORY_TOLERANCE
from psutil.tests import mock
@@ -607,11 +608,13 @@ class TestSystemCPU(unittest.TestCase):
self.assertIsNone(psutil._pslinux.cpu_count_physical())
assert m.called
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_no_result(self):
with mock.patch("psutil._pslinux.glob.glob", return_value=[]):
self.assertIsNone(psutil.cpu_freq())
@unittest.skipIf(TRAVIS, "fails on Travis")
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_use_second_file(self):
# https://github.com/giampaolo/psutil/issues/981
def glob_mock(pattern):
@@ -629,6 +632,7 @@ class TestSystemCPU(unittest.TestCase):
assert psutil.cpu_freq()
self.assertEqual(len(flags), 2)
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_emulate_data(self):
def open_mock(name, *args, **kwargs):
if name.endswith('/scaling_cur_freq'):
@@ -651,6 +655,7 @@ class TestSystemCPU(unittest.TestCase):
self.assertEqual(freq.min, 600.0)
self.assertEqual(freq.max, 700.0)
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_emulate_multi_cpu(self):
def open_mock(name, *args, **kwargs):
if name.endswith('/scaling_cur_freq'):
@@ -675,6 +680,7 @@ class TestSystemCPU(unittest.TestCase):
self.assertEqual(freq.max, 300.0)
@unittest.skipIf(TRAVIS, "fails on Travis")
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_no_scaling_cur_freq_file(self):
# See: https://github.com/giampaolo/psutil/issues/1071
def open_mock(name, *args, **kwargs):
@@ -762,21 +768,25 @@ class TestSystemNetwork(unittest.TestCase):
# Not always reliable.
# self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
self.assertEqual(stats.mtu,
- int(re.findall(r'MTU:(\d+)', out)[0]))
+ int(re.findall(r'(?i)MTU[: ](\d+)', out)[0]))
@retry_before_failing()
def test_net_io_counters(self):
def ifconfig(nic):
ret = {}
out = sh("ifconfig %s" % name)
- ret['packets_recv'] = int(re.findall(r'RX packets:(\d+)', out)[0])
- ret['packets_sent'] = int(re.findall(r'TX packets:(\d+)', out)[0])
- ret['errin'] = int(re.findall(r'errors:(\d+)', out)[0])
- ret['errout'] = int(re.findall(r'errors:(\d+)', out)[1])
- ret['dropin'] = int(re.findall(r'dropped:(\d+)', out)[0])
- ret['dropout'] = int(re.findall(r'dropped:(\d+)', out)[1])
- ret['bytes_recv'] = int(re.findall(r'RX bytes:(\d+)', out)[0])
- ret['bytes_sent'] = int(re.findall(r'TX bytes:(\d+)', out)[0])
+ ret['packets_recv'] = int(
+ re.findall(r'RX packets[: ](\d+)', out)[0])
+ ret['packets_sent'] = int(
+ re.findall(r'TX packets[: ](\d+)', out)[0])
+ ret['errin'] = int(re.findall(r'errors[: ](\d+)', out)[0])
+ ret['errout'] = int(re.findall(r'errors[: ](\d+)', out)[1])
+ ret['dropin'] = int(re.findall(r'dropped[: ](\d+)', out)[0])
+ ret['dropout'] = int(re.findall(r'dropped[: ](\d+)', out)[1])
+ ret['bytes_recv'] = int(
+ re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
+ ret['bytes_sent'] = int(
+ re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
return ret
nio = psutil.net_io_counters(pernic=True, nowrap=False)
@@ -1322,7 +1332,9 @@ class TestSensorsBattery(unittest.TestCase):
# Emulate a case where energy_full file does not exist.
# Expected fallback on /capacity.
def open_mock(name, *args, **kwargs):
- if name.startswith("/sys/class/power_supply/BAT0/energy_full"):
+ energy_full = "/sys/class/power_supply/BAT0/energy_full"
+ charge_full = "/sys/class/power_supply/BAT0/charge_full"
+ if name.startswith(energy_full) or name.startswith(charge_full):
raise IOError(errno.ENOENT, "")
elif name.startswith("/sys/class/power_supply/BAT0/capacity"):
return io.BytesIO(b"88")
@@ -1573,6 +1585,20 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.cmdline(), ['foo', 'bar', ''])
assert m.called
+ def test_cmdline_spaces_mocked(self):
+ # see: https://github.com/giampaolo/psutil/issues/1179
+ p = psutil.Process()
+ fake_file = io.StringIO(u('foo bar '))
+ with mock.patch('psutil._pslinux.open',
+ return_value=fake_file, create=True) as m:
+ self.assertEqual(p.cmdline(), ['foo', 'bar'])
+ assert m.called
+ fake_file = io.StringIO(u('foo bar '))
+ with mock.patch('psutil._pslinux.open',
+ return_value=fake_file, create=True) as m:
+ self.assertEqual(p.cmdline(), ['foo', 'bar', ''])
+ assert m.called
+
def test_readlink_path_deleted_mocked(self):
with mock.patch('psutil._pslinux.os.readlink',
return_value='/home/foo (deleted)'):
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index 85bab84c..f67c0e4c 100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -18,7 +18,6 @@ import os
import pickle
import socket
import stat
-import sys
from psutil import LINUX
from psutil import POSIX
@@ -49,6 +48,7 @@ from psutil.tests import HAS_SENSORS_TEMPERATURES
from psutil.tests import import_module_by_path
from psutil.tests import is_namedtuple
from psutil.tests import mock
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import reload_module
from psutil.tests import retry
@@ -365,6 +365,8 @@ class TestMisc(unittest.TestCase):
def test_setup_script(self):
setup_py = os.path.join(ROOT_DIR, 'setup.py')
+ if TRAVIS and not os.path.exists(setup_py):
+ return self.skipTest("can't find setup.py")
module = import_module_by_path(setup_py)
self.assertRaises(SystemExit, module.setup)
self.assertEqual(module.get_version(), psutil.__version__)
@@ -643,16 +645,20 @@ class TestWrapNumbers(unittest.TestCase):
@unittest.skipIf(TOX, "can't test on TOX")
+# See: https://travis-ci.org/giampaolo/psutil/jobs/295224806
+@unittest.skipIf(TRAVIS and not os.path.exists(SCRIPTS_DIR),
+ "can't locate scripts directory")
class TestScripts(unittest.TestCase):
"""Tests for scripts in the "scripts" directory."""
@staticmethod
- def assert_stdout(exe, args=None, **kwds):
- exe = '"%s"' % os.path.join(SCRIPTS_DIR, exe)
- if args:
- exe = exe + ' ' + args
+ def assert_stdout(exe, *args, **kwargs):
+ exe = '%s' % os.path.join(SCRIPTS_DIR, exe)
+ cmd = [PYTHON_EXE, exe]
+ for arg in args:
+ cmd.append(arg)
try:
- out = sh(sys.executable + ' ' + exe, **kwds).strip()
+ out = sh(cmd, **kwargs).strip()
except RuntimeError as err:
if 'AccessDenied' in str(err):
return str(err)
@@ -700,7 +706,7 @@ class TestScripts(unittest.TestCase):
self.assert_stdout('meminfo.py')
def test_procinfo(self):
- self.assert_stdout('procinfo.py', args=str(os.getpid()))
+ self.assert_stdout('procinfo.py', str(os.getpid()))
# can't find users on APPVEYOR or TRAVIS
@unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(),
@@ -724,7 +730,7 @@ class TestScripts(unittest.TestCase):
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
def test_pmap(self):
- self.assert_stdout('pmap.py', args=str(os.getpid()))
+ self.assert_stdout('pmap.py', str(os.getpid()))
@unittest.skipIf(not HAS_MEMORY_FULL_INFO, "not supported")
def test_procsmem(self):
@@ -743,7 +749,7 @@ class TestScripts(unittest.TestCase):
self.assert_syntax('iotop.py')
def test_pidof(self):
- output = self.assert_stdout('pidof.py', args=psutil.Process().name())
+ output = self.assert_stdout('pidof.py', psutil.Process().name())
self.assertIn(str(os.getpid()), output)
@unittest.skipIf(not WINDOWS, "WINDOWS only")
@@ -1014,7 +1020,8 @@ class TestNetUtils(unittest.TestCase):
# work around http://bugs.python.org/issue30204
types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1
self.assertGreaterEqual(fams[socket.AF_INET], 2)
- self.assertGreaterEqual(fams[socket.AF_INET6], 2)
+ if supports_ipv6():
+ self.assertGreaterEqual(fams[socket.AF_INET6], 2)
if POSIX and HAS_CONNECTIONS_UNIX:
self.assertGreaterEqual(fams[socket.AF_UNIX], 2)
self.assertGreaterEqual(types[socket.SOCK_STREAM], 2)
diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py
index c8214f14..bcb2ba4e 100755
--- a/psutil/tests/test_osx.py
+++ b/psutil/tests/test_osx.py
@@ -14,6 +14,7 @@ import psutil
from psutil import OSX
from psutil.tests import create_zombie_proc
from psutil.tests import get_test_subprocess
+from psutil.tests import HAS_BATTERY
from psutil.tests import MEMORY_TOLERANCE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
@@ -285,6 +286,18 @@ class TestSystemAPIs(unittest.TestCase):
self.assertEqual(stats.mtu,
int(re.findall(r'mtu (\d+)', out)[0]))
+ # --- sensors_battery
+
+ @unittest.skipIf(not HAS_BATTERY, "no battery")
+ def test_sensors_battery(self):
+ out = sh("pmset -g batt")
+ percent = re.search("(\d+)%", out).group(1)
+ drawing_from = re.search("Now drawing from '([^']+)'", out).group(1)
+ power_plugged = drawing_from == "AC Power"
+ psutil_result = psutil.sensors_battery()
+ self.assertEqual(psutil_result.power_plugged, power_plugged)
+ self.assertEqual(psutil_result.percent, int(percent))
+
if __name__ == '__main__':
run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py
index 580abdfd..c59f9a1c 100755
--- a/psutil/tests/test_posix.py
+++ b/psutil/tests/test_posix.py
@@ -10,11 +10,13 @@
import datetime
import errno
import os
+import re
import subprocess
import sys
import time
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import LINUX
from psutil import OPENBSD
@@ -27,7 +29,7 @@ from psutil.tests import APPVEYOR
from psutil.tests import get_kernel_version
from psutil.tests import get_test_subprocess
from psutil.tests import mock
-from psutil.tests import PYTHON
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
from psutil.tests import run_test_module_by_name
@@ -46,8 +48,9 @@ def ps(cmd):
if not LINUX:
cmd = cmd.replace(" --no-headers ", " ")
if SUNOS:
- cmd = cmd.replace("-o command", "-o comm")
cmd = cmd.replace("-o start", "-o stime")
+ if AIX:
+ cmd = cmd.replace("-o rss", "-o rssize")
output = sh(cmd)
if not LINUX:
output = output.split('\n')[1].strip()
@@ -56,6 +59,31 @@ def ps(cmd):
except ValueError:
return output
+# ps "-o" field names differ wildly between platforms.
+# "comm" means "only executable name" but is not available on BSD platforms.
+# "args" means "command with all its arguments", and is also not available
+# on BSD platforms.
+# "command" is like "args" on most platforms, but like "comm" on AIX,
+# and not available on SUNOS.
+# so for the executable name we can use "comm" on Solaris and split "command"
+# on other platforms.
+# to get the cmdline (with args) we have to use "args" on AIX and
+# Solaris, and can use "command" on all others.
+
+
+def ps_name(pid):
+ field = "command"
+ if SUNOS:
+ field = "comm"
+ return ps("ps --no-headers -o %s -p %s" % (field, pid)).split(' ')[0]
+
+
+def ps_args(pid):
+ field = "command"
+ if AIX or SUNOS:
+ field = "args"
+ return ps("ps --no-headers -o %s -p %s" % (field, pid))
+
@unittest.skipIf(not POSIX, "POSIX only")
class TestProcess(unittest.TestCase):
@@ -63,7 +91,7 @@ class TestProcess(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.pid = get_test_subprocess([PYTHON, "-E", "-O"],
+ cls.pid = get_test_subprocess([PYTHON_EXE, "-E", "-O"],
stdin=subprocess.PIPE).pid
wait_for_pid(cls.pid)
@@ -121,12 +149,14 @@ class TestProcess(unittest.TestCase):
self.assertEqual(vsz_ps, vsz_psutil)
def test_name(self):
- # use command + arg since "comm" keyword not supported on all platforms
- name_ps = ps("ps --no-headers -o command -p %s" % (
- self.pid)).split(' ')[0]
+ name_ps = ps_name(self.pid)
# remove path if there is any, from the command
name_ps = os.path.basename(name_ps).lower()
name_psutil = psutil.Process(self.pid).name().lower()
+ # ...because of how we calculate PYTHON_EXE; on OSX this may
+ # be "pythonX.Y".
+ name_ps = re.sub(r"\d.\d", "", name_ps)
+ name_psutil = re.sub(r"\d.\d", "", name_psutil)
self.assertEqual(name_ps, name_psutil)
def test_name_long(self):
@@ -179,8 +209,7 @@ class TestProcess(unittest.TestCase):
self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp])
def test_exe(self):
- ps_pathname = ps("ps --no-headers -o command -p %s" %
- self.pid).split(' ')[0]
+ ps_pathname = ps_name(self.pid)
psutil_pathname = psutil.Process(self.pid).exe()
try:
self.assertEqual(ps_pathname, psutil_pathname)
@@ -195,18 +224,17 @@ class TestProcess(unittest.TestCase):
self.assertEqual(ps_pathname, adjusted_ps_pathname)
def test_cmdline(self):
- ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid)
+ ps_cmdline = ps_args(self.pid)
psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline())
- if SUNOS:
- # ps on Solaris only shows the first part of the cmdline
- psutil_cmdline = psutil_cmdline.split(" ")[0]
self.assertEqual(ps_cmdline, psutil_cmdline)
# On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an
# incorrect value (20); the real deal is getpriority(2) which
# returns 0; psutil relies on it, see:
# https://github.com/giampaolo/psutil/issues/1082
+ # AIX has the same issue
@unittest.skipIf(SUNOS, "not reliable on SUNOS")
+ @unittest.skipIf(AIX, "not reliable on AIX")
def test_nice(self):
ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid)
psutil_nice = psutil.Process().nice()
@@ -262,7 +290,7 @@ class TestSystemAPIs(unittest.TestCase):
def test_pids(self):
# Note: this test might fail if the OS is starting/killing
# other processes in the meantime
- if SUNOS:
+ if SUNOS or AIX:
cmd = ["ps", "-A", "-o", "pid"]
else:
cmd = ["ps", "ax", "-o", "pid"]
@@ -285,11 +313,7 @@ class TestSystemAPIs(unittest.TestCase):
# on OSX and OPENBSD ps doesn't show pid 0
if OSX or OPENBSD and 0 not in pids_ps:
pids_ps.insert(0, 0)
-
- if pids_ps != pids_psutil:
- difference = [x for x in pids_psutil if x not in pids_ps] + \
- [x for x in pids_ps if x not in pids_psutil]
- self.fail("difference: " + str(difference))
+ self.assertEqual(pids_ps, pids_psutil)
# for some reason ifconfig -a does not report all interfaces
# returned by psutil
@@ -355,6 +379,8 @@ class TestSystemAPIs(unittest.TestCase):
psutil._psposix.wait_pid, os.getpid())
assert m.called
+ # AIX can return '-' in df output instead of numbers, e.g. for /proc
+ @unittest.skipIf(AIX, "unreliable on AIX")
def test_disk_usage(self):
def df(device):
out = sh("df -k %s" % device).strip()
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index 8b88f766..3b60d38a 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -9,6 +9,7 @@
import collections
import errno
import getpass
+import itertools
import os
import signal
import socket
@@ -22,6 +23,7 @@ import types
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import LINUX
from psutil import NETBSD
@@ -49,9 +51,10 @@ from psutil.tests import HAS_MEMORY_MAPS
from psutil.tests import HAS_PROC_CPU_NUM
from psutil.tests import HAS_PROC_IO_COUNTERS
from psutil.tests import HAS_RLIMIT
+from psutil.tests import HAS_THREADS
from psutil.tests import mock
from psutil.tests import PYPY
-from psutil.tests import PYTHON
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
from psutil.tests import run_test_module_by_name
@@ -62,7 +65,6 @@ from psutil.tests import skip_on_not_implemented
from psutil.tests import TESTFILE_PREFIX
from psutil.tests import TESTFN
from psutil.tests import ThreadTask
-from psutil.tests import TOX
from psutil.tests import TRAVIS
from psutil.tests import unittest
from psutil.tests import wait_for_pid
@@ -151,7 +153,7 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGKILL)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
sproc = get_test_subprocess()
@@ -161,12 +163,12 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGTERM)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
# check sys.exit() code
code = "import time, sys; time.sleep(0.01); sys.exit(5);"
- sproc = get_test_subprocess([PYTHON, "-c", code])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
p = psutil.Process(sproc.pid)
self.assertEqual(p.wait(), 5)
self.assertFalse(p.is_running())
@@ -175,7 +177,7 @@ class TestProcess(unittest.TestCase):
# It is not supposed to raise NSP when the process is gone.
# On UNIX this should return None, on Windows it should keep
# returning the exit code.
- sproc = get_test_subprocess([PYTHON, "-c", code])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
p = psutil.Process(sproc.pid)
self.assertEqual(p.wait(), 5)
self.assertIn(p.wait(), (5, None))
@@ -207,8 +209,8 @@ class TestProcess(unittest.TestCase):
# to get None.
self.assertEqual(ret2, None)
else:
- self.assertEqual(ret1, 0)
- self.assertEqual(ret1, 0)
+ self.assertEqual(ret1, signal.SIGTERM)
+ self.assertEqual(ret1, signal.SIGTERM)
def test_wait_timeout_0(self):
sproc = get_test_subprocess()
@@ -227,7 +229,7 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGKILL)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
def test_cpu_percent(self):
@@ -316,10 +318,10 @@ class TestProcess(unittest.TestCase):
# test reads
io1 = p.io_counters()
- with open(PYTHON, 'rb') as f:
+ with open(PYTHON_EXE, 'rb') as f:
f.read()
io2 = p.io_counters()
- if not BSD:
+ if not BSD and not AIX:
self.assertGreater(io2.read_count, io1.read_count)
self.assertEqual(io2.write_count, io1.write_count)
if LINUX:
@@ -528,6 +530,7 @@ class TestProcess(unittest.TestCase):
p = psutil.Process()
self.assertGreater(p.num_handles(), 0)
+ @unittest.skipIf(not HAS_THREADS, 'not supported')
def test_threads(self):
p = psutil.Process()
if OPENBSD:
@@ -552,6 +555,7 @@ class TestProcess(unittest.TestCase):
@retry_before_failing()
@skip_on_access_denied(only_if=OSX)
+ @unittest.skipIf(not HAS_THREADS, 'not supported')
def test_threads_2(self):
sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
@@ -693,12 +697,12 @@ class TestProcess(unittest.TestCase):
sproc = get_test_subprocess()
exe = psutil.Process(sproc.pid).exe()
try:
- self.assertEqual(exe, PYTHON)
+ self.assertEqual(exe, PYTHON_EXE)
except AssertionError:
- if WINDOWS and len(exe) == len(PYTHON):
+ if WINDOWS and len(exe) == len(PYTHON_EXE):
# on Windows we don't care about case sensitivity
normcase = os.path.normcase
- self.assertEqual(normcase(exe), normcase(PYTHON))
+ self.assertEqual(normcase(exe), normcase(PYTHON_EXE))
else:
# certain platforms such as BSD are more accurate returning:
# "/usr/local/bin/python2.7"
@@ -709,7 +713,7 @@ class TestProcess(unittest.TestCase):
ver = "%s.%s" % (sys.version_info[0], sys.version_info[1])
try:
self.assertEqual(exe.replace(ver, ''),
- PYTHON.replace(ver, ''))
+ PYTHON_EXE.replace(ver, ''))
except AssertionError:
# Tipically OSX. Really not sure what to do here.
pass
@@ -718,7 +722,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(out, 'hey')
def test_cmdline(self):
- cmdline = [PYTHON, "-c", "import time; time.sleep(60)"]
+ cmdline = [PYTHON_EXE, "-c", "import time; time.sleep(60)"]
sproc = get_test_subprocess(cmdline)
try:
self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()),
@@ -728,27 +732,39 @@ class TestProcess(unittest.TestCase):
# and Open BSD returns a truncated string.
# Also /proc/pid/cmdline behaves the same so it looks
# like this is a kernel bug.
- if NETBSD or OPENBSD:
+ # XXX - AIX truncates long arguments in /proc/pid/cmdline
+ if NETBSD or OPENBSD or AIX:
self.assertEqual(
- psutil.Process(sproc.pid).cmdline()[0], PYTHON)
+ psutil.Process(sproc.pid).cmdline()[0], PYTHON_EXE)
else:
raise
def test_name(self):
- sproc = get_test_subprocess(PYTHON)
+ sproc = get_test_subprocess(PYTHON_EXE)
name = psutil.Process(sproc.pid).name().lower()
pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
assert pyexe.startswith(name), (pyexe, name)
# XXX
@unittest.skipIf(SUNOS, "broken on SUNOS")
+ @unittest.skipIf(AIX, "broken on AIX")
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
+
+ def rm():
+ # Try to limit occasional failures on Appveyor:
+ # https://ci.appveyor.com/project/giampaolo/psutil/build/1350/
+ # job/lbo3bkju55le850n
+ try:
+ safe_rmpath(funky_path)
+ except OSError:
+ pass
+
funky_path = TESTFN + 'foo bar )'
create_exe(funky_path)
- self.addCleanup(safe_rmpath, funky_path)
+ self.addCleanup(rm)
cmdline = [funky_path, "-c",
"import time; [time.sleep(0.01) for x in range(3000)];"
"arg1", "arg2", "", "arg3", ""]
@@ -853,7 +869,8 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.cwd(), os.getcwd())
def test_cwd_2(self):
- cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(60)"]
+ cmd = [PYTHON_EXE, "-c",
+ "import os, time; os.chdir('..'); time.sleep(60)"]
sproc = get_test_subprocess(cmd)
p = psutil.Process(sproc.pid)
call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
@@ -870,10 +887,9 @@ class TestProcess(unittest.TestCase):
self.assertEqual(len(initial), len(set(initial)))
all_cpus = list(range(len(psutil.cpu_percent(percpu=True))))
- # setting on travis doesn't seem to work (always return all
- # CPUs on get):
- # AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, ... != [0]
- for n in all_cpus:
+ # Work around travis failure:
+ # https://travis-ci.org/giampaolo/psutil/builds/284173194
+ for n in all_cpus if not TRAVIS else initial:
p.cpu_affinity([n])
self.assertEqual(p.cpu_affinity(), [n])
if hasattr(os, "sched_getaffinity"):
@@ -911,6 +927,24 @@ class TestProcess(unittest.TestCase):
self.assertRaises(TypeError, p.cpu_affinity, [0, "1"])
self.assertRaises(ValueError, p.cpu_affinity, [0, -1])
+ @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported')
+ def test_cpu_affinity_all_combinations(self):
+ p = psutil.Process()
+ initial = p.cpu_affinity()
+ assert initial, initial
+ self.addCleanup(p.cpu_affinity, initial)
+
+ # All possible CPU set combinations.
+ combos = []
+ for l in range(0, len(initial) + 1):
+ for subset in itertools.combinations(initial, l):
+ if subset:
+ combos.append(list(subset))
+
+ for combo in combos:
+ p.cpu_affinity(combo)
+ self.assertEqual(p.cpu_affinity(), combo)
+
# TODO: #595
@unittest.skipIf(BSD, "broken on BSD")
# can't find any process file on Appveyor
@@ -937,7 +971,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])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", cmdline])
p = psutil.Process(sproc.pid)
for x in range(100):
@@ -1268,7 +1302,15 @@ class TestProcess(unittest.TestCase):
# set methods
succeed_or_zombie_p_exc(zproc.parent)
if hasattr(zproc, 'cpu_affinity'):
- succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+ try:
+ succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+ except ValueError as err:
+ if TRAVIS and LINUX and "not eligible" in str(err):
+ # https://travis-ci.org/giampaolo/psutil/jobs/279890461
+ pass
+ else:
+ raise
+
succeed_or_zombie_p_exc(zproc.nice, 0)
if hasattr(zproc, 'ionice'):
if LINUX:
@@ -1294,10 +1336,14 @@ class TestProcess(unittest.TestCase):
# self.assertEqual(zpid.ppid(), os.getpid())
# ...and all other APIs should be able to deal with it
self.assertTrue(psutil.pid_exists(zpid))
- self.assertIn(zpid, psutil.pids())
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
- psutil._pmap = {}
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+ if not TRAVIS and OSX:
+ # For some reason this started failing all of the sudden.
+ # Maybe they upgraded OSX version?
+ # https://travis-ci.org/giampaolo/psutil/jobs/310896404
+ self.assertIn(zpid, psutil.pids())
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+ psutil._pmap = {}
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
@unittest.skipIf(not POSIX, 'POSIX only')
def test_zombie_process_is_running_w_exc(self):
@@ -1360,28 +1406,23 @@ class TestProcess(unittest.TestCase):
@unittest.skipIf(not HAS_ENVIRON, "not supported")
def test_environ(self):
+ def clean_dict(d):
+ # Most of these are problematic on Travis.
+ d.pop("PSUTIL_TESTING", None)
+ d.pop("PLAT", None)
+ d.pop("HOME", None)
+ if OSX:
+ d.pop("__CF_USER_TEXT_ENCODING", None)
+ d.pop("VERSIONER_PYTHON_PREFER_32_BIT", None)
+ d.pop("VERSIONER_PYTHON_VERSION", None)
+ return dict(
+ [(k.rstrip("\r\n"), v.rstrip("\r\n")) for k, v in d.items()])
+
self.maxDiff = None
p = psutil.Process()
- d = p.environ()
- d2 = os.environ.copy()
-
- removes = []
- if 'PSUTIL_TESTING' in os.environ:
- removes.append('PSUTIL_TESTING')
- if OSX:
- removes.extend([
- "__CF_USER_TEXT_ENCODING",
- "VERSIONER_PYTHON_PREFER_32_BIT",
- "VERSIONER_PYTHON_VERSION"])
- if LINUX or OSX:
- removes.extend(['PLAT'])
- if TOX:
- removes.extend(['HOME'])
- for key in removes:
- d.pop(key, None)
- d2.pop(key, None)
-
- self.assertEqual(d, d2)
+ d1 = clean_dict(p.environ())
+ d2 = clean_dict(os.environ.copy())
+ self.assertEqual(d1, d2)
@unittest.skipIf(not HAS_ENVIRON, "not supported")
@unittest.skipIf(not POSIX, "POSIX only")
@@ -1485,7 +1526,7 @@ class TestPopen(unittest.TestCase):
# XXX this test causes a ResourceWarning on Python 3 because
# psutil.__subproc instance doesn't get propertly freed.
# Not sure what to do though.
- cmd = [PYTHON, "-c", "import time; time.sleep(60);"]
+ cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
with psutil.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
proc.name()
@@ -1496,7 +1537,7 @@ class TestPopen(unittest.TestCase):
proc.terminate()
def test_ctx_manager(self):
- with psutil.Popen([PYTHON, "-V"],
+ with psutil.Popen([PYTHON_EXE, "-V"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE) as proc:
@@ -1510,7 +1551,7 @@ class TestPopen(unittest.TestCase):
# subprocess.Popen()'s terminate(), kill() and send_signal() do
# not raise exception after the process is gone. psutil.Popen
# diverges from that.
- cmd = [PYTHON, "-c", "import time; time.sleep(60);"]
+ cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
with psutil.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
proc.terminate()
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index e93bb6b5..20b132a9 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -19,6 +19,7 @@ import tempfile
import time
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import FREEBSD
from psutil import LINUX
@@ -669,7 +670,8 @@ 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, "unreliable on APPVEYOR") # no visible disks
+ @unittest.skipIf(APPVEYOR and psutil.disk_io_counters() is None,
+ "unreliable on APPVEYOR") # no visible disks
def test_disk_io_counters(self):
def check_ntuple(nt):
self.assertEqual(nt[0], nt.read_count)
@@ -689,6 +691,7 @@ class TestSystemAPIs(unittest.TestCase):
assert getattr(nt, name) >= 0, nt
ret = psutil.disk_io_counters(perdisk=False)
+ assert ret is not None, "no disks on this system?"
check_ntuple(ret)
ret = psutil.disk_io_counters(perdisk=True)
# make sure there are no duplicates
@@ -742,7 +745,8 @@ class TestSystemAPIs(unittest.TestCase):
for name in infos._fields:
value = getattr(infos, name)
self.assertGreaterEqual(value, 0)
- if name in ('ctx_switches', 'interrupts'):
+ # on AIX, ctx_switches is always 0
+ if not AIX and name in ('ctx_switches', 'interrupts'):
self.assertGreater(value, 0)
@unittest.skipIf(not HAS_CPU_FREQ, "not suported")
diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py
index 9b99fdf9..c2a2f847 100755
--- a/psutil/tests/test_unicode.py
+++ b/psutil/tests/test_unicode.py
@@ -49,7 +49,7 @@ etc.) and make sure that:
For a detailed explanation of how psutil handles unicode see:
- https://github.com/giampaolo/psutil/issues/1040
-- https://pythonhosted.org/psutil/#unicode
+- http://psutil.readthedocs.io/#unicode
"""
import os
@@ -91,8 +91,6 @@ import psutil.tests
def safe_rmpath(path):
- # XXX
- return _safe_rmpath(path)
if APPVEYOR:
# TODO - this is quite random and I'm not sure why it happens,
# nor I can reproduce it locally:
@@ -125,8 +123,9 @@ def subprocess_supports_unicode(name):
except UnicodeEncodeError:
return False
else:
- reap_children()
return True
+ finally:
+ reap_children()
# An invalid unicode string.
@@ -145,18 +144,23 @@ else:
class _BaseFSAPIsTests(object):
funky_name = None
- def setUp(self):
- safe_rmpath(self.funky_name)
+ @classmethod
+ def setUpClass(cls):
+ safe_rmpath(cls.funky_name)
+ create_exe(cls.funky_name)
+
+ @classmethod
+ def tearDownClass(cls):
+ reap_children()
+ safe_rmpath(cls.funky_name)
def tearDown(self):
reap_children()
- safe_rmpath(self.funky_name)
def expect_exact_path_match(self):
raise NotImplementedError("must be implemented in subclass")
def test_proc_exe(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
p = psutil.Process(subp.pid)
exe = p.exe()
@@ -165,7 +169,6 @@ class _BaseFSAPIsTests(object):
self.assertEqual(exe, self.funky_name)
def test_proc_name(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
if WINDOWS:
# On Windows name() is determined from exe() first, because
@@ -182,7 +185,6 @@ class _BaseFSAPIsTests(object):
self.assertEqual(name, os.path.basename(self.funky_name))
def test_proc_cmdline(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
p = psutil.Process(subp.pid)
cmdline = p.cmdline()
@@ -192,18 +194,20 @@ class _BaseFSAPIsTests(object):
self.assertEqual(cmdline, [self.funky_name])
def test_proc_cwd(self):
- safe_mkdir(self.funky_name)
- with chdir(self.funky_name):
+ dname = self.funky_name + "2"
+ self.addCleanup(safe_rmpath, dname)
+ safe_mkdir(dname)
+ with chdir(dname):
p = psutil.Process()
cwd = p.cwd()
self.assertIsInstance(p.cwd(), str)
if self.expect_exact_path_match():
- self.assertEqual(cwd, self.funky_name)
+ self.assertEqual(cwd, dname)
def test_proc_open_files(self):
p = psutil.Process()
start = set(p.open_files())
- with open(self.funky_name, 'wb'):
+ with open(self.funky_name, 'rb'):
new = set(p.open_files())
path = (new - start).pop().path
self.assertIsInstance(path, str)
@@ -260,8 +264,10 @@ class _BaseFSAPIsTests(object):
self.assertEqual(conn.laddr, name)
def test_disk_usage(self):
- safe_mkdir(self.funky_name)
- psutil.disk_usage(self.funky_name)
+ dname = self.funky_name + "2"
+ self.addCleanup(safe_rmpath, dname)
+ safe_mkdir(dname)
+ psutil.disk_usage(dname)
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
@unittest.skipIf(not PY3, "ctypes does not support unicode on PY2")
@@ -335,6 +341,9 @@ class TestWinProcessName(unittest.TestCase):
class TestNonFSAPIS(unittest.TestCase):
"""Unicode tests for non fs-related APIs."""
+ def tearDown(self):
+ reap_children()
+
@unittest.skipIf(not HAS_ENVIRON, "not supported")
def test_proc_environ(self):
# Note: differently from others, this test does not deal
diff --git a/scripts/ifconfig.py b/scripts/ifconfig.py
index b823b374..e2a9ce53 100755
--- a/scripts/ifconfig.py
+++ b/scripts/ifconfig.py
@@ -10,34 +10,34 @@ A clone of 'ifconfig' on UNIX.
$ python scripts/ifconfig.py
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
+ incoming : bytes=1.95M, pkts=22158, errs=0, drops=0
+ outgoing : bytes=1.95M, pkts=22158, 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
-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
+docker0:
+ stats : speed=0MB, duplex=?, mtu=1500, up=yes
+ incoming : bytes=3.48M, pkts=65470, errs=0, drops=0
+ outgoing : bytes=164.06M, pkts=112993, errs=0, drops=0
+ IPv4 address : 172.17.0.1
+ broadcast : 172.17.0.1
+ netmask : 255.255.0.0
+ IPv6 address : fe80::42:27ff:fe5e:799e%docker0
netmask : ffff:ffff:ffff:ffff::
- MAC address : 0a:00:27:00:00:00
+ MAC address : 02:42:27:5e:79:9e
broadcast : ff:ff:ff:ff:ff:ff
-eth0:
+wlp3s0:
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
+ incoming : bytes=7.04G, pkts=5637208, errs=0, drops=0
+ outgoing : bytes=372.01M, pkts=3200026, errs=0, drops=0
+ IPv4 address : 10.0.0.2
broadcast : 10.255.255.255
netmask : 255.0.0.0
- IPv6 address : fe80::7592:1dcf:bcb7:98d6%wlp3s0
+ IPv6 address : fe80::ecb3:1584:5d17:937%wlp3s0
netmask : ffff:ffff:ffff:ffff::
MAC address : 48:45:20:59:a4:0c
broadcast : ff:ff:ff:ff:ff:ff
@@ -62,6 +62,24 @@ duplex_map = {
}
+def bytes2human(n):
+ """
+ >>> bytes2human(10000)
+ '9.8 K'
+ >>> bytes2human(100001221)
+ '95.4 M'
+ """
+ symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+ prefix = {}
+ for i, s in enumerate(symbols):
+ prefix[s] = 1 << (i + 1) * 10
+ for s in reversed(symbols):
+ if n >= prefix[s]:
+ value = float(n) / prefix[s]
+ return '%.2f%s' % (value, s)
+ return '%.2fB' % (n)
+
+
def main():
stats = psutil.net_if_stats()
io_counters = psutil.net_io_counters(pernic=True)
@@ -77,10 +95,12 @@ def main():
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))
+ bytes2human(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))
+ bytes2human(io.bytes_sent), io.packets_sent, io.errout,
+ io.dropout))
for addr in addrs:
print(" %-4s" % af_map.get(addr.family, addr.family), end="")
print(" address : %s" % addr.address)
diff --git a/scripts/internal/download_exes.py b/scripts/internal/download_exes.py
index 9688919b..1b004428 100755
--- a/scripts/internal/download_exes.py
+++ b/scripts/internal/download_exes.py
@@ -25,7 +25,7 @@ from psutil import __version__ as PSUTIL_VERSION
BASE_URL = 'https://ci.appveyor.com/api'
-PY_VERSIONS = ['2.7', '3.3', '3.4', '3.5', '3.6']
+PY_VERSIONS = ['2.7', '3.4', '3.5', '3.6']
TIMEOUT = 30
COLORS = True
@@ -170,8 +170,8 @@ def main(options):
completed += 1
print("downloaded %-45s %s" % (
local_fname, bytes2human(os.path.getsize(local_fname))))
- # 2 exes (32 and 64 bit) and 2 wheels (32 and 64 bit) for each ver.
- expected = len(PY_VERSIONS) * 4
+ # 2 wheels (32 and 64 bit) per supported python version
+ expected = len(PY_VERSIONS) * 2
if expected != completed:
return exit("expected %s files, got %s" % (expected, completed))
if exc:
diff --git a/scripts/internal/generate_manifest.py b/scripts/internal/generate_manifest.py
index 8b6b4f5f..3511b749 100755
--- a/scripts/internal/generate_manifest.py
+++ b/scripts/internal/generate_manifest.py
@@ -12,6 +12,10 @@ import os
import subprocess
+IGNORED_EXTS = ('.png', '.jpg', '.jpeg')
+IGNORED_FILES = ('.travis.yml', 'appveyor.yml')
+
+
def sh(cmd):
return subprocess.check_output(
cmd, shell=True, universal_newlines=True).strip()
@@ -21,8 +25,8 @@ def main():
files = sh("git ls-files").split('\n')
for file in files:
if file.startswith('.ci/') or \
- os.path.splitext(file)[1] in ('.png', '.jpg') or \
- file in ('.travis.yml', 'appveyor.yml'):
+ os.path.splitext(file)[1].lower() in IGNORED_EXTS or \
+ file in IGNORED_FILES:
continue
print("include " + file)
diff --git a/scripts/internal/print_announce.py b/scripts/internal/print_announce.py
index e47911c2..1c2b9e11 100755
--- a/scripts/internal/print_announce.py
+++ b/scripts/internal/print_announce.py
@@ -19,7 +19,7 @@ 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_DOC = 'http://psutil.readthedocs.io'
PRJ_URL_DOWNLOAD = 'https://pypi.python.org/pypi/psutil'
PRJ_URL_WHATSNEW = \
'https://github.com/giampaolo/psutil/blob/master/HISTORY.rst'
@@ -39,10 +39,9 @@ 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.
+currently supports Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD, NetBSD \
+and AIX, both 32-bit and 64-bit architectures, with Python versions from 2.6 \
+to 3.6. PyPy is also known to work.
What's new
==========
diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py
index 138a0b0c..548f7a8e 100755
--- a/scripts/internal/winmake.py
+++ b/scripts/internal/winmake.py
@@ -174,6 +174,12 @@ def recursive_rm(*patterns):
safe_rmtree(os.path.join(root, dir))
+def test_setup():
+ os.environ['PYTHONWARNINGS'] = 'all'
+ os.environ['PSUTIL_TESTING'] = '1'
+ os.environ['PSUTIL_DEBUG'] = '1'
+
+
# ===================================================================
# commands
# ===================================================================
@@ -205,13 +211,6 @@ def build():
@cmd
-def build_exe():
- """Create exe file."""
- build()
- sh("%s setup.py bdist_wininst" % PYTHON)
-
-
-@cmd
def build_wheel():
"""Create wheel file."""
build()
@@ -328,15 +327,15 @@ def flake8():
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 -Wa -m flake8 %s" % (PYTHON, py_files), nolog=True)
+ sh("%s -m flake8 %s" % (PYTHON, py_files), nolog=True)
@cmd
def test():
"""Run tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa %s" % (PYTHON, TSCRIPT))
+ test_setup()
+ sh("%s %s" % (PYTHON, TSCRIPT))
@cmd
@@ -344,8 +343,8 @@ def coverage():
"""Run coverage tests."""
# Note: coverage options are controlled by .coveragerc file
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m coverage run %s" % (PYTHON, TSCRIPT))
+ test_setup()
+ 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)
@@ -355,56 +354,56 @@ def coverage():
def test_process():
"""Run process tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_process" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_process" % PYTHON)
@cmd
def test_system():
"""Run system tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_system" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_system" % PYTHON)
@cmd
def test_platform():
"""Run windows only tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_windows" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_windows" % PYTHON)
@cmd
def test_misc():
"""Run misc tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_misc" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_misc" % PYTHON)
@cmd
def test_unicode():
"""Run unicode tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_unicode" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_unicode" % PYTHON)
@cmd
def test_connections():
"""Run connections tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_connections" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_connections" % PYTHON)
@cmd
def test_contracts():
"""Run contracts tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v psutil.tests.test_contracts" % PYTHON)
+ test_setup()
+ sh("%s -m unittest -v psutil.tests.test_contracts" % PYTHON)
@cmd
@@ -416,8 +415,8 @@ def test_by_name():
except IndexError:
sys.exit('second arg missing')
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa -m unittest -v %s" % (PYTHON, name))
+ test_setup()
+ sh("%s -m unittest -v %s" % (PYTHON, name))
@cmd
@@ -429,16 +428,16 @@ def test_script():
except IndexError:
sys.exit('second arg missing')
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa %s" % (PYTHON, name))
+ test_setup()
+ sh("%s %s" % (PYTHON, name))
@cmd
def test_memleaks():
"""Run memory leaks tests"""
install()
- os.environ['PSUTIL_TESTING'] = '1'
- sh("%s -Wa psutil\\tests\\test_memory_leaks.py" % PYTHON)
+ test_setup()
+ sh("%s psutil\\tests\\test_memory_leaks.py" % PYTHON)
@cmd
@@ -467,8 +466,8 @@ def set_python(s):
# try to look for a python installation
orig = s
s = s.replace('.', '')
- vers = ('26', '27', '33', '34', '35', '36', '37',
- '26-64', '27-64', '33-64', '34-64', '35-64', '36-64', '37-64')
+ vers = ('26', '27', '34', '35', '36', '37',
+ '26-64', '27-64', '34-64', '35-64', '36-64', '37-64')
for v in vers:
if s == v:
path = 'C:\\python%s\python.exe' % s
diff --git a/scripts/procinfo.py b/scripts/procinfo.py
index d8625560..54205de3 100755
--- a/scripts/procinfo.py
+++ b/scripts/procinfo.py
@@ -225,7 +225,8 @@ def run(pid, verbose=False):
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 'num_ctx_switches' in pinfo:
+ print_("ctx-switches", str_ntuple(pinfo['num_ctx_switches']))
if pinfo['children']:
template = "%-6s %s"
print_("children", template % ("PID", "NAME"))
diff --git a/setup.py b/setup.py
index a0ce34d6..1625a3eb 100755
--- a/setup.py
+++ b/setup.py
@@ -4,10 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""psutil is a cross-platform library for retrieving information on
-running processes and system utilization (CPU, memory, disks, network)
-in Python.
-"""
+"""Cross-platform lib for process and system monitoring in Python."""
import contextlib
import io
@@ -40,6 +37,7 @@ from _common import OSX # NOQA
from _common import POSIX # NOQA
from _common import SUNOS # NOQA
from _common import WINDOWS # NOQA
+from _common import AIX # NOQA
macros = []
@@ -242,7 +240,18 @@ elif SUNOS:
],
define_macros=macros,
libraries=['kstat', 'nsl', 'socket'])
-
+# AIX
+elif AIX:
+ macros.append(("PSUTIL_AIX", 1))
+ ext = Extension(
+ 'psutil._psutil_aix',
+ sources=sources + [
+ 'psutil/_psutil_aix.c',
+ 'psutil/arch/aix/net_connections.c',
+ 'psutil/arch/aix/common.c',
+ 'psutil/arch/aix/ifaddrs.c'],
+ libraries=['perfstat'],
+ define_macros=macros)
else:
sys.exit('platform %s is not supported' % sys.platform)
@@ -257,6 +266,8 @@ if POSIX:
if platform.release() == '5.10':
posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c')
posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1))
+ elif AIX:
+ posix_extension.sources.append('psutil/arch/aix/ifaddrs.c')
extensions = [ext, posix_extension]
else:
@@ -267,7 +278,7 @@ def main():
kwargs = dict(
name='psutil',
version=VERSION,
- description=__doc__ .replace('\n', '').strip() if __doc__ else '',
+ description=__doc__ .replace('\n', ' ').strip() if __doc__ else '',
long_description=get_description(),
keywords=[
'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty',
@@ -307,10 +318,6 @@ def main():
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.0',
- 'Programming Language :: Python :: 3.1',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',