Bug 1196253 - update in-tree psutil to 3.1.1. r=gps
authorJoel Maher <jmaher@mozilla.com>
Thu, 20 Aug 2015 08:03:31 -0400
changeset 286873 2eea7ab5228dde61fdc8706ea8452df10da6dd1d
parent 286872 5d168ed7999b8f4c3472a822eddafd2b630870a5
child 286874 04416351b24c2b1d38fc80af743624ce7ec43ea8
push id4617
push userdburns@mozilla.com
push dateThu, 20 Aug 2015 22:26:19 +0000
reviewersgps
bugs1196253
milestone43.0a1
Bug 1196253 - update in-tree psutil to 3.1.1. r=gps
python/psutil/CREDITS
python/psutil/HISTORY.rst
python/psutil/INSTALL.rst
python/psutil/MANIFEST.in
python/psutil/Makefile
python/psutil/PKG-INFO
python/psutil/README.rst
python/psutil/TODO
python/psutil/docs/conf.py
python/psutil/docs/index.rst
python/psutil/docs/xxx
python/psutil/examples/disk_usage.py
python/psutil/examples/free.py
python/psutil/examples/ifconfig.py
python/psutil/examples/iotop.py
python/psutil/examples/meminfo.py
python/psutil/examples/netstat.py
python/psutil/examples/nettop.py
python/psutil/examples/pidof.py
python/psutil/examples/pmap.py
python/psutil/examples/process_detail.py
python/psutil/examples/ps.py
python/psutil/examples/pstree.py
python/psutil/examples/top.py
python/psutil/examples/who.py
python/psutil/make.bat
python/psutil/psutil.egg-info/PKG-INFO
python/psutil/psutil.egg-info/SOURCES.txt
python/psutil/psutil.egg-info/dependency_links.txt
python/psutil/psutil.egg-info/top_level.txt
python/psutil/psutil/__init__.py
python/psutil/psutil/_common.py
python/psutil/psutil/_compat.py
python/psutil/psutil/_psbsd.py
python/psutil/psutil/_pslinux.py
python/psutil/psutil/_psosx.py
python/psutil/psutil/_psposix.py
python/psutil/psutil/_pssunos.py
python/psutil/psutil/_psutil_bsd.c
python/psutil/psutil/_psutil_bsd.h
python/psutil/psutil/_psutil_linux.c
python/psutil/psutil/_psutil_linux.h
python/psutil/psutil/_psutil_osx.c
python/psutil/psutil/_psutil_posix.c
python/psutil/psutil/_psutil_posix.h
python/psutil/psutil/_psutil_sunos.c
python/psutil/psutil/_psutil_sunos.h
python/psutil/psutil/_psutil_windows.c
python/psutil/psutil/_psutil_windows.h
python/psutil/psutil/_pswindows.py
python/psutil/psutil/arch/bsd/process_info.c
python/psutil/psutil/arch/osx/process_info.c
python/psutil/psutil/arch/windows/inet_ntop.c
python/psutil/psutil/arch/windows/inet_ntop.h
python/psutil/psutil/arch/windows/ntextapi.h
python/psutil/psutil/arch/windows/process_handles.c
python/psutil/psutil/arch/windows/process_handles.h
python/psutil/psutil/arch/windows/process_info.c
python/psutil/psutil/arch/windows/process_info.h
python/psutil/psutil/arch/windows/security.c
python/psutil/setup.py
python/psutil/test/README
python/psutil/test/README.rst
python/psutil/test/__init__.py
python/psutil/test/_bsd.py
python/psutil/test/_linux.py
python/psutil/test/_osx.py
python/psutil/test/_posix.py
python/psutil/test/_sunos.py
python/psutil/test/_windows.py
python/psutil/test/test_memory_leaks.py
python/psutil/test/test_psutil.py
python/psutil/tox.ini
--- a/python/psutil/CREDITS
+++ b/python/psutil/CREDITS
@@ -250,14 +250,61 @@ E: szigeti.gabor.niif@gmail.com
 I: 446
 
 N: msabramo
 E: msabramo@gmail.com
 I: 492
 
 N: Jeff Tang
 W: https://github.com/mrjefftang
-I: 340, 529
+I: 340, 529, 616, 653, 654
 
 N: Yaolong Huang
 E: airekans@gmail.com
 W: http://airekans.github.io/
 I: 530
+
+N: Anders Chrigström
+W: https://github.com/anders-chrigstrom
+I: 496
+
+N: spacewander
+E: spacewanderlzx@gmail.com
+I: 561
+
+N: Sylvain Mouquet
+E: sylvain.mouquet@gmail.com
+I: 565
+
+N: karthikrev
+I: 568
+
+N: Bruno Binet
+E: bruno.binet@gmail.com
+I: 572
+
+N: Gabi Davar
+C: Israel
+W: https://github.com/mindw
+I: 578, 581, 587
+
+N: spacewanderlzx
+C: Guangzhou,China
+E: spacewanderlzx@gmail.com
+I: 555
+
+N: Fabian Groffen
+I: 611, 618
+
+N: desbma
+W: https://github.com/desbma
+C: France
+I: 628
+
+N: John Burnett
+W: http://www.johnburnett.com/
+C: Irvine, CA, US
+I: 614
+
+N: Árni Már Jónsson
+E: Reykjavik, Iceland
+E: https://github.com/arnimarj
+I: 634
--- a/python/psutil/HISTORY.rst
+++ b/python/psutil/HISTORY.rst
@@ -1,15 +1,148 @@
 Bug tracker at https://github.com/giampaolo/psutil/issues
 
-2.1.3 2014-09-26
-================
+3.1.1 - 2015-07-15
+==================
+
+**Bug fixes**
+
+- #645: [Linux] psutil.cpu_times_percent() may produce negative results.
+- #656: 'from psutil import *' does not work.
+
+
+3.1.0 - 2015-07-15
+==================
+
+**Enhancements**
+
+- #534: [Linux] disk_partitions() added support for ZFS filesystems.
+- #646: continuous tests integration for Windows with
+  https://ci.appveyor.com/project/giampaolo/psutil.
+- #647: new dev guide:
+  https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst
+- #651: continuous code quality test integration with
+  https://scrutinizer-ci.com/g/giampaolo/psutil/
+
+**Bug fixes**
+
+- #340: [Windows] Process.open_files() no longer hangs. Instead it uses a
+  thred which times out and skips the file handle in case it's taking too long
+  to be retrieved.  (patch by Jeff Tang, PR #597)
+- #627: [Windows] Process.name() no longer raises AccessDenied for pids owned
+  by another user.
+- #636: [Windows] Process.memory_info() raise AccessDenied.
+- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it
+  will affect os.getpid()'s process group instead of PID 0.
+- #639: [Linux] Process.cmdline() can be truncated.
+- #640: [Linux] *connections functions may swallow errors and return an
+  incomplete list of connnections.
+- #642: repr() of exceptions is incorrect.
+- #653: [Windows] Add inet_ntop function for Windows XP to support IPv6.
+- #641: [Windows] Replace deprecated string functions with safe equivalents.
+
+
+3.0.1 - 2015-06-18
+==================
 
 **Bug fixes**
 
+- #632: [Linux] better error message if cannot parse process UNIX connections.
+- #634: [Linux] Proces.cmdline() does not include empty string arguments.
+- #635: [UNIX] crash on module import if 'enum' package is installed on python
+  < 3.4.
+
+
+3.0.0 - 2015-06-13
+==================
+
+**Enhancements**
+
+- #250: new psutil.net_if_stats() returning NIC statistics (isup, duplex,
+  speed, MTU).
+- #376: new psutil.net_if_addrs() returning all NIC addresses a-la ifconfig.
+- #469: on Python >= 3.4 ``IOPRIO_CLASS_*`` and ``*_PRIORITY_CLASS`` constants
+  returned by psutil.Process' ionice() and nice() methods are enums instead of
+  plain integers.
+- #581: add .gitignore. (patch by Gabi Davar)
+- #582: connection constants returned by psutil.net_connections() and
+  psutil.Process.connections() were turned from int to enums on Python > 3.4.
+- #587: Move native extension into the package.
+- #589: Process.cpu_affinity() accepts any kind of iterable (set, tuple, ...),
+  not only lists.
+- #594: all deprecated APIs were removed.
+- #599: [Windows] process name() can now be determined for all processes even
+  when running as a limited user.
+- #602: pre-commit GIT hook.
+- #629: enhanced support for py.test and nose test discovery and tests run.
+- #616: [Windows] Add inet_ntop function for Windows XP.
+
+**Bug fixes**
+
+- #428: [all UNIXes except Linux] correct handling of zombie processes;
+  introduced new ZombieProcess exception class.
+- #512: [BSD] fix segfault in net_connections().
+- #555: [Linux] psutil.users() correctly handles ":0" as an alias for
+  "localhost"
+- #579: [Windows] Fixed open_files() for PID>64K.
+- #579: [Windows] fixed many compiler warnings.
+- #585: [FreeBSD] net_connections() may raise KeyError.
+- #586: [FreeBSD] cpu_affinity() segfaults on set in case an invalid CPU
+  number is provided.
+- #593: [FreeBSD] Process().memory_maps() segfaults.
+- #606: Process.parent() may swallow NoSuchProcess exceptions.
+- #611: [SunOS] net_io_counters has send and received swapped
+- #614: [Linux]: cpu_count(logical=False) return the number of physical CPUs
+  instead of physical cores.
+- #618: [SunOS] swap tests fail on Solaris when run as normal user
+- #628: [Linux] Process.name() truncates process name in case it contains
+  spaces or parentheses.
+
+
+2.2.1 - 2015-02-02
+==================
+
+**Bug fixes**
+
+- #496: [Linux] fix "ValueError: ambiguos inode with multiple PIDs references"
+  (patch by Bruno Binet)
+
+
+2.2.0 - 2015-01-06
+==================
+
+**Enhancements**
+
+- #521: drop support for Python 2.4 and 2.5.
+- #553: new examples/pstree.py script.
+- #564: C extension version mismatch in case the user messed up with psutil
+  installation or with sys.path is now detected at import time.
+- #568: New examples/pidof.py script.
+- #569: [FreeBSD] add support for process CPU affinity.
+
+**Bug fixes**
+
+- #496: [Solaris] can't import psutil.
+- #547: [UNIX] Process.username() may raise KeyError if UID can't be resolved.
+- #551: [Windows] get rid of the unicode hack for net_io_counters() NIC names.
+- #556: [Linux] lots of file handles were left open.
+- #561: [Linux] net_connections() might skip some legitimate UNIX sockets.
+  (patch by spacewander)
+- #565: [Windows] use proper encoding for psutil.Process.username() and
+  psutil.users(). (patch by Sylvain Mouquet)
+- #567: [Linux] in the alternative implementation of CPU affinity PyList_Append
+  and Py_BuildValue return values are not checked.
+- #569: [FreeBSD] fix memory leak in psutil.cpu_count(logical=False).
+- #571: [Linux] Process.open_files() might swallow AccessDenied exceptions and
+  return an incomplete list of open files.
+
+
+2.1.3 - 2014-09-26
+==================
+
 - #536: [Linux]: fix "undefined symbol: CPU_ALLOC" compilation error.
 
 
 2.1.2 - 2014-09-21
 ==================
 
 **Enhancements**
 
--- a/python/psutil/INSTALL.rst
+++ b/python/psutil/INSTALL.rst
@@ -1,144 +1,116 @@
 ============================
 Installing using pip on UNIX
 ============================
 
 The easiest way to install psutil on UNIX is by using pip (but first you might
 need to install python header files; see later).
-First install pip:
+First install pip::
 
-    $ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
-    python ez_setup.py
+    $ wget https://bootstrap.pypa.io/get-pip.py
+    $ python get-pip.py
 
-...then run:
+...then run::
 
     $ pip install psutil
 
 You may need to install gcc and python header files first (see later).
 
 
 =====================
 Installing on Windows
 =====================
 
 Just get the right installer for your Python version and architecture from:
 https://pypi.python.org/pypi/psutil/#downloads
-
-
-==================================
-Compiling on Windows using mingw32
-==================================
-
-First install mingw (http://www.mingw.org/) then add mingw "bin" folder to
-environment PATH (NOTE: this assumes MinGW is installed in C:\MinGW):
-
-    SET PATH=C:\MinGW\bin;%PATH%
-
-You can then compile psutil by running:
-
-    setup.py build -c mingw32
-
-To compile and install:
-
-    setup.py build -c mingw32 install
-
-You can also use make.bat which automatically sets the env variable for you:
-
-    make.bat build
-
-FWIW I managed to compile psutil against all 32-bit Python versions but not
-64 bit.
+Since wheels installers are also available you may also use pip.
 
 
 ========================================
 Compiling on Windows using Visual Studio
 ========================================
 
-To use Visual Studio to compile psutil you must have the same version of
-Visual Studio used to compile your installation of Python which is::
+In order to compile psutil on Windows you'll need Visual Studio (Mingw32 is
+no longer supported). You must have the same version of Visual Studio used to compile
+your installation of Python, that is::
 
-    Python 2.4:  VS 2003
-    Python 2.5:  VS 2003
-    Python 2.6:  VS 2008
-    Python 2.7:  VS 2008
-    Python 3.3+: VS 2010
+* Python 2.6:  VS 2008
+* Python 2.7:  VS 2008
+* Python 3.3, 3.4: VS 2010 (you can download it from `MS website <http://www.visualstudio.com/downloads/download-visual-studio-vs#d-2010-express>`_)
+* Python 3.5: `VS 2015 UP <http://www.visualstudio.com/en-au/news/vs2015-preview-vs>`_
 
-...then run:
+...then run::
 
     setup.py build
 
-...or:
+...or::
 
     make.bat build
 
 Compiling 64 bit versions of Python 2.6 and 2.7 with VS 2008 requires
 Windows SDK and .NET Framework 3.5 SP1 to be installed first.
 Once you have those run vcvars64.bat, then compile:
 http://stackoverflow.com/questions/11072521/
 
-If you do not have the right version of Visual Studio available then try using
-MinGW instead.
-
-
 ===================
 Installing on Linux
 ===================
 
 gcc is required and so the python headers. They can easily be installed by
-using the distro package manager. For example, on Debian amd Ubuntu:
+using the distro package manager. For example, on Debian and Ubuntu::
 
     $ sudo apt-get install gcc python-dev
 
-...on Redhat and CentOS:
+...on Redhat and CentOS::
 
     $ sudo yum install gcc python-devel
 
-Once done, you can build/install psutil with:
+Once done, you can build/install psutil with::
 
     $ python setup.py install
 
 
 ==================
 Installing on OS X
 ==================
 
 OS X installation from source will require gcc which you can obtain as part of
 the 'XcodeTools' installer from Apple. Then you can run the standard distutils
 commands.
-To build only:
+To build only::
 
     $ python setup.py build
 
-To install and build:
+To install and build::
 
     $ python setup.py install
 
 
 =====================
 Installing on FreeBSD
 =====================
 
 The same compiler used to install Python must be present on the system in order
 to build modules using distutils. Assuming it is installed, you can build using
 the standard distutils commands.
 
-Build only:
+Build only::
 
     $ python setup.py build
 
-Install and build:
+Install and build::
 
     $ python setup.py install
 
 
 ========
 Makefile
 ========
 
 A makefile is available for both UNIX and Windows (make.bat).  It provides
 some automations for the tasks described above and might be preferred over
 using setup.py. With it you can::
 
-    $ make install    # just install
+    $ make install    # just install (in --user mode)
     $ make uninstall  # uninstall (needs pip)
     $ make test       # run tests
     $ make clean      # remove installation files
--- a/python/psutil/MANIFEST.in
+++ b/python/psutil/MANIFEST.in
@@ -1,17 +1,22 @@
+include .coveragerc
+include .git-pre-commit
+include .git-pre-commit
+include .gitignore
 include .travis.yml
 include CREDITS
 include HISTORY.rst
 include INSTALL.rst
 include LICENSE
 include make.bat
 include Makefile
 include MANIFEST.in
 include README.rst
 include setup.py
 include TODO
 include tox.ini
+recursive-exclude docs/_build *
+recursive-include .appveyor/*
 recursive-include docs *
-recursive-exclude docs/_build *
 recursive-include examples *.py
 recursive-include psutil *.py *.c *.h
-recursive-include test *.py README
\ No newline at end of file
+recursive-include test *.py README*
--- a/python/psutil/Makefile
+++ b/python/psutil/Makefile
@@ -11,80 +11,112 @@ all: test
 clean:
 	rm -f `find . -type f -name \*.py[co]`
 	rm -f `find . -type f -name \*.so`
 	rm -f `find . -type f -name .\*~`
 	rm -f `find . -type f -name \*.orig`
 	rm -f `find . -type f -name \*.bak`
 	rm -f `find . -type f -name \*.rej`
 	rm -rf `find . -type d -name __pycache__`
+	rm -rf *.core
 	rm -rf *.egg-info
 	rm -rf *\$testfile*
+	rm -rf .coverage
 	rm -rf .tox
 	rm -rf build
 	rm -rf dist
 	rm -rf docs/_build
+	rm -rf htmlcov
 
 build: clean
 	$(PYTHON) setup.py build
+	@# copies *.so files in ./psutil directory in order to allow
+	@# "import psutil" when using the interactive interpreter from within
+	@# this directory.
+	$(PYTHON) setup.py build_ext -i
+
+# useful deps which are nice to have while developing / testing
+setup-dev-env:
+	python -c "import urllib2; \
+			   r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \
+			   open('/tmp/get-pip.py', 'w').write(r.read());"
+	$(PYTHON) /tmp/get-pip.py --user
+	rm /tmp/get-pip.py
+	$(PYTHON) -m pip install --user --upgrade pip
+	$(PYTHON) -m pip install --user --upgrade \
+		coverage  \
+		flake8 \
+		ipaddress \
+		ipdb \
+		mock==1.0.1 \
+		nose \
+		pep8 \
+		pyflakes \
+		sphinx \
+		sphinx-pypi-upload \
+		unittest2 \
 
 install: build
-	if test $(PYTHON) = python2.4; then \
-		$(PYTHON) setup.py install; \
-	elif test $(PYTHON) = python2.5; then \
-		$(PYTHON) setup.py install; \
-	else \
-		$(PYTHON) setup.py install --user; \
-	fi
+	$(PYTHON) setup.py install --user
 
 uninstall:
-	if test $(PYTHON) = python2.4; then \
-		pip-2.4 uninstall -y -v psutil; \
-	else \
-		cd ..; $(PYTHON) -m pip uninstall -y -v psutil; \
-	fi
+	cd ..; $(PYTHON) -m pip uninstall -y -v psutil
 
 test: install
 	$(PYTHON) $(TSCRIPT)
 
 test-process: install
 	$(PYTHON) -m unittest -v test.test_psutil.TestProcess
 
 test-system: install
 	$(PYTHON) -m unittest -v test.test_psutil.TestSystemAPIs
 
 test-memleaks: install
-	$(PYTHON) -m unittest -v test.test_memory_leaks
+	$(PYTHON) test/test_memory_leaks.py
 
 # Run a specific test by name; e.g. "make test-by-name disk_" will run
 # all test methods containing "disk_" in their name.
 # Requires "pip install nose".
-test-by-name:
-	@$(PYTHON) -m nose test/test_psutil.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
+test-by-name: install
+	@$(PYTHON) -m nose test/test_psutil.py test/_* --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
+
+# Same as above but for test_memory_leaks.py script.
+test-memleaks-by-name: install
+	@$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS))
 
-# requires "pip install pep8"
+coverage: install
+	# Note: coverage options are controlled by .coveragerc file
+	rm -rf .coverage htmlcov
+	$(PYTHON) -m coverage run $(TSCRIPT)
+	$(PYTHON) -m coverage report
+	@echo "writing results to htmlcov/index.html"
+	$(PYTHON) -m coverage html
+	$(PYTHON) -m webbrowser -t htmlcov/index.html
+
 pep8:
-	@git ls-files | grep \\.py$ | xargs pep8
+	@git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8
 
-# requires "pip install pyflakes"
 pyflakes:
 	@export PYFLAKES_NODOCTEST=1 && \
-		git ls-files | grep \\.py$ | xargs pyflakes
+		git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes
 
-# requires "pip install flake8"
 flake8:
-	@git ls-files | grep \\.py$ | xargs flake8
-
+	@git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8
 
 # Upload source tarball on https://pypi.python.org/pypi/psutil.
 upload-src: clean
 	$(PYTHON) setup.py sdist upload
 
 # Build and upload doc on https://pythonhosted.org/psutil/.
 # Requires "pip install sphinx-pypi-upload".
 upload-doc:
 	cd docs; make html
 	$(PYTHON) setup.py upload_sphinx --upload-dir=docs/_build/html
 
 # git-tag a new release
 git-tag-release:
 	git tag -a release-`python -c "import setup; print(setup.get_version())"` -m `git rev-list HEAD --count`:`git rev-parse --short HEAD`
 	echo "done; now run 'git push --follow-tags' to push the new tag on the remote repo"
+
+# install GIT pre-commit hook
+install-git-hooks:
+	ln -sf ../../.git-pre-commit .git/hooks/pre-commit
+	chmod +x .git/hooks/pre-commit
--- a/python/psutil/PKG-INFO
+++ b/python/psutil/PKG-INFO
@@ -1,57 +1,76 @@
 Metadata-Version: 1.1
 Name: psutil
-Version: 2.1.3
+Version: 3.1.1
 Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python.
 Home-page: https://github.com/giampaolo/psutil
 Author: Giampaolo Rodola
 Author-email: g.rodola <at> gmail <dot> com
 License: BSD
-Description: .. image:: https://pypip.in/d/psutil/badge.png
-            :target: https://crate.io/packages/psutil/
-            :alt: Download this month
+Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg
+            :target: https://pypi.python.org/pypi/psutil#downloads
+            :alt: Downloads this month
+        
+        .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
+            :target: https://travis-ci.org/giampaolo/psutil
+            :alt: Linux tests (Travis)
         
-        .. image:: https://pypip.in/v/psutil/badge.png
+        .. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
+            :target: https://ci.appveyor.com/project/giampaolo/psutil
+            :alt: Windows tests (Appveyor)
+        
+        .. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github
+            :target: https://coveralls.io/github/giampaolo/psutil?branch=master
+            :alt: Test coverage (coverall.io)
+        
+        .. image:: https://img.shields.io/pypi/v/psutil.svg
             :target: https://pypi.python.org/pypi/psutil/
             :alt: Latest version
         
-        .. image:: https://pypip.in/license/psutil/badge.png
+        .. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
+            :target: https://github.com/giampaolo/psutil/
+            :alt: Github stars
+        
+        .. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg
+            :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
+            :alt: Code quality (scrutinizer-ci.com)
+        
+        .. image:: https://img.shields.io/pypi/l/psutil.svg
             :target: https://pypi.python.org/pypi/psutil/
             :alt: License
         
-        .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
-            :target: https://travis-ci.org/giampaolo/psutil
-            :alt: Travis
-        
         ===========
         Quick links
         ===========
         
         - `Home page <https://github.com/giampaolo/psutil>`_
         - `Documentation <http://pythonhosted.org/psutil/>`_
+        - `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
         - `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>`_
+        - `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
         - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
         
         =======
         Summary
         =======
         
         psutil (python system and process utilities) is a cross-platform library for
         retrieving information on **running processes** and **system utilization**
         (CPU, memory, disks, network) in Python. It is useful mainly for **system
         monitoring**, **profiling and limiting process resources** and **management of
         running processes**. It implements many functionalities offered by command line
         tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
         ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
         **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and
-        **64-bit** architectures, with Python versions from **2.4 to 3.4**. PyPy is
-        also known to work.
+        **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 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+        `PyPy <http://pypy.org/>`__ is also known to work.
         
         ====================
         Example applications
         ====================
         
         .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
             :target: http://psutil.googlecode.com/svn/wiki/images/top.png
             :alt: top
@@ -62,18 +81,18 @@ Description: .. image:: https://pypip.in
         
         .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
             :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
             :alt: iotop
         
         See also:
         
          * https://github.com/nicolargo/glances
+         * https://github.com/google/grr
          * https://github.com/Jahaja/psdash
-         * https://code.google.com/p/grr/
         
         ==============
         Example usages
         ==============
         
         CPU
         ===
         
@@ -112,19 +131,19 @@ Description: .. image:: https://pypip.in
             >>>
         
         Memory
         ======
         
         .. code-block:: python
         
             >>> psutil.virtual_memory()
-            svmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L, free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L, cached=1251086336)
+            svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
             >>> psutil.swap_memory()
-            sswap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1, sin=304193536, sout=677842944)
+            sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
             >>>
         
         Disks
         =====
         
         .. code-block:: python
         
             >>> psutil.disk_partitions()
@@ -143,21 +162,33 @@ Description: .. image:: https://pypip.in
         
         .. code-block:: python
         
             >>> psutil.net_io_counters(pernic=True)
             {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
              'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
             >>>
             >>> psutil.net_connections()
-            [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-             pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-             pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-             pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
+             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
+             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
+             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
              ...]
+            >>>
+            >>> psutil.net_if_addrs()
+            {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
+                    snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
+                    snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
+             'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
+                       snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
+                       snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+            >>>
+            >>> psutil.net_if_stats()
+            {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+             'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
         
         Other system info
         =================
         
         .. code-block:: python
         
             >>> psutil.users()
             [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
@@ -213,37 +244,37 @@ Description: .. image:: https://pypip.in
             [0, 1, 2, 3]
             >>> p.cpu_affinity([0])  # set
             >>>
             >>> p.memory_percent()
             0.63423
             >>>
             >>> p.memory_info()
             pmem(rss=7471104, vms=68513792)
-            >>> p.ext_memory_info()
+            >>> p.memory_info_ex()
             extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
             >>> p.memory_maps()
             [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
              pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
              pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
              pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
              pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
              ...]
             >>>
             >>> p.io_counters()
             pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
             >>>
             >>> p.open_files()
             [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
             >>>
             >>> p.connections()
-            [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-             pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-             pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-             pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
+             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
+             pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
+             pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
             >>>
             >>> p.num_threads()
             4
             >>> p.num_fds()
             8
             >>> p.threads()
             [pthread(id=5234, user_time=22.5, system_time=9.2891),
              pthread(id=5235, user_time=0.0, system_time=0.0),
@@ -254,17 +285,17 @@ Description: .. image:: https://pypip.in
             pctxsw(voluntary=78, involuntary=19)
             >>>
             >>> p.nice()
             0
             >>> p.nice(10)  # set
             >>>
             >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)
             >>> p.ionice()
-            pionice(ioclass=3, value=0)
+            pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
             >>>
             >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))  # set resource limits (Linux only)
             >>> p.rlimit(psutil.RLIMIT_NOFILE)
             (5, 5)
             >>>
             >>> p.suspend()
             >>> p.resume()
             >>>
@@ -322,16 +353,22 @@ Description: .. image:: https://pypip.in
         ============
         
         http://groups.google.com/group/psutil/
         
         ========
         Timeline
         ========
         
+        - 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
+        - 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
+        - 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
+        - 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
+        - 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
+        - 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
         - 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
         - 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
         - 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
         - 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
         - 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
         - 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
         - 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
         - 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
@@ -351,17 +388,17 @@ Description: .. image:: https://pypip.in
         - 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
         - 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
         - 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
         - 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
         - 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
         - 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
         - 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
         
-Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,monitoring,ulimit,prlimit
+Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit
 Platform: Platform Independent
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Console
 Classifier: Environment :: Win32 (MS Windows)
 Classifier: Intended Audience :: Developers
 Classifier: Intended Audience :: Information Technology
 Classifier: Intended Audience :: System Administrators
 Classifier: License :: OSI Approved :: BSD License
@@ -370,18 +407,16 @@ Classifier: Operating System :: Microsof
 Classifier: Operating System :: Microsoft
 Classifier: Operating System :: OS Independent
 Classifier: Operating System :: POSIX :: BSD :: FreeBSD
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Operating System :: POSIX :: SunOS/Solaris
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: C
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.4
-Classifier: Programming Language :: Python :: 2.5
 Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.0
 Classifier: Programming Language :: Python :: 3.1
 Classifier: Programming Language :: Python :: 3.2
 Classifier: Programming Language :: Python :: 3.3
 Classifier: Programming Language :: Python :: 3.4
--- a/python/psutil/README.rst
+++ b/python/psutil/README.rst
@@ -1,49 +1,68 @@
-.. image:: https://pypip.in/d/psutil/badge.png
-    :target: https://crate.io/packages/psutil/
-    :alt: Download this month
+.. image:: https://img.shields.io/pypi/dm/psutil.svg
+    :target: https://pypi.python.org/pypi/psutil#downloads
+    :alt: Downloads this month
+
+.. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
+    :target: https://travis-ci.org/giampaolo/psutil
+    :alt: Linux tests (Travis)
 
-.. image:: https://pypip.in/v/psutil/badge.png
+.. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
+    :target: https://ci.appveyor.com/project/giampaolo/psutil
+    :alt: Windows tests (Appveyor)
+
+.. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github
+    :target: https://coveralls.io/github/giampaolo/psutil?branch=master
+    :alt: Test coverage (coverall.io)
+
+.. image:: https://img.shields.io/pypi/v/psutil.svg
     :target: https://pypi.python.org/pypi/psutil/
     :alt: Latest version
 
-.. image:: https://pypip.in/license/psutil/badge.png
+.. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
+    :target: https://github.com/giampaolo/psutil/
+    :alt: Github stars
+
+.. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg
+    :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
+    :alt: Code quality (scrutinizer-ci.com)
+
+.. image:: https://img.shields.io/pypi/l/psutil.svg
     :target: https://pypi.python.org/pypi/psutil/
     :alt: License
 
-.. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
-    :target: https://travis-ci.org/giampaolo/psutil
-    :alt: Travis
-
 ===========
 Quick links
 ===========
 
 - `Home page <https://github.com/giampaolo/psutil>`_
 - `Documentation <http://pythonhosted.org/psutil/>`_
+- `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
 - `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>`_
+- `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
 - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
 
 =======
 Summary
 =======
 
 psutil (python system and process utilities) is a cross-platform library for
 retrieving information on **running processes** and **system utilization**
 (CPU, memory, disks, network) in Python. It is useful mainly for **system
 monitoring**, **profiling and limiting process resources** and **management of
 running processes**. It implements many functionalities offered by command line
 tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
 ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
 **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and
-**64-bit** architectures, with Python versions from **2.4 to 3.4**. PyPy is
-also known to work.
+**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 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+`PyPy <http://pypy.org/>`__ is also known to work.
 
 ====================
 Example applications
 ====================
 
 .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
     :target: http://psutil.googlecode.com/svn/wiki/images/top.png
     :alt: top
@@ -54,18 +73,18 @@ Example applications
 
 .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
     :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
     :alt: iotop
 
 See also:
 
  * https://github.com/nicolargo/glances
+ * https://github.com/google/grr
  * https://github.com/Jahaja/psdash
- * https://code.google.com/p/grr/
 
 ==============
 Example usages
 ==============
 
 CPU
 ===
 
@@ -104,19 +123,19 @@ CPU
     >>>
 
 Memory
 ======
 
 .. code-block:: python
 
     >>> psutil.virtual_memory()
-    svmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L, free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L, cached=1251086336)
+    svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
     >>> psutil.swap_memory()
-    sswap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1, sin=304193536, sout=677842944)
+    sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
     >>>
 
 Disks
 =====
 
 .. code-block:: python
 
     >>> psutil.disk_partitions()
@@ -135,21 +154,33 @@ Network
 
 .. code-block:: python
 
     >>> psutil.net_io_counters(pernic=True)
     {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
      'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
     >>>
     >>> psutil.net_connections()
-    [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-     pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-     pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-     pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
+     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
      ...]
+    >>>
+    >>> psutil.net_if_addrs()
+    {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
+            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
+            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
+     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
+               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
+               snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+    >>>
+    >>> psutil.net_if_stats()
+    {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+     'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
 
 Other system info
 =================
 
 .. code-block:: python
 
     >>> psutil.users()
     [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
@@ -205,37 +236,37 @@ Process management
     [0, 1, 2, 3]
     >>> p.cpu_affinity([0])  # set
     >>>
     >>> p.memory_percent()
     0.63423
     >>>
     >>> p.memory_info()
     pmem(rss=7471104, vms=68513792)
-    >>> p.ext_memory_info()
+    >>> p.memory_info_ex()
     extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
     >>> p.memory_maps()
     [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
      pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
      pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
      pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
      pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
      ...]
     >>>
     >>> p.io_counters()
     pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
     >>>
     >>> p.open_files()
     [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
     >>>
     >>> p.connections()
-    [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-     pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-     pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-     pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
+     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
+     pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
+     pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
     >>>
     >>> p.num_threads()
     4
     >>> p.num_fds()
     8
     >>> p.threads()
     [pthread(id=5234, user_time=22.5, system_time=9.2891),
      pthread(id=5235, user_time=0.0, system_time=0.0),
@@ -246,17 +277,17 @@ Process management
     pctxsw(voluntary=78, involuntary=19)
     >>>
     >>> p.nice()
     0
     >>> p.nice(10)  # set
     >>>
     >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)
     >>> p.ionice()
-    pionice(ioclass=3, value=0)
+    pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
     >>>
     >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))  # set resource limits (Linux only)
     >>> p.rlimit(psutil.RLIMIT_NOFILE)
     (5, 5)
     >>>
     >>> p.suspend()
     >>> p.resume()
     >>>
@@ -314,16 +345,22 @@ Mailing list
 ============
 
 http://groups.google.com/group/psutil/
 
 ========
 Timeline
 ========
 
+- 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
+- 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
+- 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
+- 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
+- 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
+- 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
 - 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
 - 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
 - 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
 - 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
 - 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
 - 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
 - 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
 - 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
--- a/python/psutil/TODO
+++ b/python/psutil/TODO
@@ -4,78 +4,83 @@ TODO
 A collection of ideas and notes about stuff to implement in future versions.
 "#NNN" occurrences refer to bug tracker issues at:
 https://github.com/giampaolo/psutil/issues
 
 
 HIGHER PRIORITY
 ===============
 
- * #250: net ifaces speed.
-
- * #376: ifconfig functionalities aka psutil.net_ifaces (could be merged
-         with #250)
-
  * OpenBSD support.
 
  * #371: CPU temperature (apparently OSX and Linux only; on Linux it requires
    lm-sensors lib).
 
- * #269: expose network ifaces RX/TW queues.
+ * #269: expose network ifaces RX/TW queues. This should probably go into
+   net_if_stats(). Figure out on what platforms this is supported:
+   Linux: yes
+   Others: ?
 
- * Process.threads(): thread names
+ * Process.threads(): thread names; patch for OSX available at:
+   https://code.google.com/p/plcrashreporter/issues/detail?id=65
 
  * Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964)
 
+ * (Windows) fall back on using WMIC for Process methods returning AccessDenied
+
+ * #613: thread names.
+
+ * #604: emulate os.getloadavg() on Windows
+
+ * #269: NIC rx/tx queue.
+
 
 LOWER PRIORITY
 ==============
 
  * #355: Android support.
 
  * #276: GNU/Hurd support.
 
  * #429: NetBSD support.
 
  * DragonFlyBSD support?
 
  * AIX support?
 
- * examples/pidof.py (same as 'pidof' cli tool)
-
- * examples/pstree.py (same as 'pstree' cli tool)
-    * threads() should also return thread names in order to implement it
-
  * examples/taskmgr-gui.py (using tk).
 
  * system-wide number of open file descriptors:
     * https://jira.hyperic.com/browse/SIGAR-30
     * http://www.netadmintools.com/part295.html
 
  * Number of system threads.
     * Windows: http://msdn.microsoft.com/en-us/library/windows/desktop/ms684824(v=vs.85).aspx
 
  * #357: what CPU a process is on.
 
- * thread names:
-    * https://code.google.com/p/plcrashreporter/issues/detail?id=65
-
- * Doc / wiki which compares similarities between UNIX cli tools and psutil.
+  * Doc / wiki which compares similarities between UNIX cli tools and psutil.
    Example:
    df -a  ->  psutil.disk_partitions
    lsof   ->  psutil.Process.open_files() and psutil.Process.open_connections()
    killall->  (actual script)
    tty    ->  psutil.Process.terminal()
    who    ->  psutil.users()
 
 
 DEBATABLE
 =========
 
- * support wheels? http://pythonwheels.com/
+ * psutil.proc_tree() something which obtains a {pid:ppid, ...} dict for
+   all running processes in one shot. This can be factored out from
+   Process.children() and exposed as a first class function.
+   PROS: on Windows we can take advantage of _psutil_windows.ppid_map()
+   which is faster than iterating over all pids and calling ppid().
+   CONS: examples/pstree.py shows this can be easily done in the user code
+   so maybe it's not worth the addition.
 
  * advanced cmdline interface exposing the whole API and providing different
    kind of outputs (e.g. pprinted, colorized, json).
 
  * [Linux]: process cgroups (http://en.wikipedia.org/wiki/Cgroups). They look
    similar to prlimit() in terms of functionality but uglier (they should allow
    limiting per-process network IO resources though, which is great). Needs
    further reading.
@@ -86,20 +91,28 @@ DEBATABLE
    http://docs.python.org/dev/whatsnew/3.3.html#os
    http://bugs.python.org/issue12655
    http://docs.python.org/dev/library/os.html#interface-to-the-scheduler
    It might be worth to take a look and figure out whether we can include some
    of those in psutil.
    Also, we can probably reimplement wait_pid() on POSIX which is currently
    implemented as a busy-loop.
 
- * Certain systems (XXX figure out which ones exactly) provide CPU times about
-   process children. On those systems Process.cpu_times() might return
-   a (user, system, user_children, system_children) ntuple.
-   Also, os.times() provides 'elapsed' times as well.
+ * Certain systems provide CPU times about process children. On those systems
+   Process.cpu_times() might return a (user, system, user_children,
+   system_children) ntuple.
+   * Linux: /proc/{PID}/stat
+   * Solaris: pr_cutime and pr_cstime
+   * FreeBSD: none
+   * OSX: none
+   * Windows: none
+
+ * ...also, os.times() provides 'elapsed' times as well.
+
+ * ...also Linux provides guest_time and cguest_time.
 
  * Enrich exception classes hierarchy on Python >= 3.3 / post PEP-3151 so that:
    - NoSuchProcess inherits from ProcessLookupError
    - AccessDenied inherits from PermissionError
    - TimeoutExpired inherits from TimeoutError (debatable)
    See: http://docs.python.org/3/library/exceptions.html#os-exceptions
 
  * Process.threads() might grow an extra "id" parameter so that it can be
@@ -125,16 +138,20 @@ DEBATABLE
     attribute attached to the returned Process instances so that one can avoid
     catching NSP and AccessDenied:
         for p in process_iter(attrs=['cpu_percent']):
             print(p.cache['cpu_percent'])
     This also leads questions as whether we should introduce a sorting order.
 
   * round Process.memory_percent() result?
 
+  * #550: number of threads per core.
+
+  * Have psutil.Process().cpu_affinity([]) be an alias for "all CPUs"?
+
 
 COMPATIBILITY BREAKAGE
 ======================
 
 Removals (will likely happen in 2.2):
 
  * (S) psutil.Process.nice  (deprecated in 0.5.0)
  * (S) get_process_list  (deprecated in 0.5.0)
@@ -142,9 +159,9 @@ Removals (will likely happen in 2.2):
  * (M) psutil.network_io_counters  (deprecated in 1.0.0)
  * (M) local_address and remote_address Process.connection() namedtuple fields
        (deprecated in 1.0.0)
 
 
 REJECTED IDEAS
 ==============
 
-STUB
\ No newline at end of file
+STUB
--- a/python/psutil/docs/conf.py
+++ b/python/psutil/docs/conf.py
@@ -9,50 +9,36 @@
 # Note that not all possible configuration values are present in this
 # autogenerated file.
 #
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
 import datetime
 import os
-import sys
 
 
-if sys.version_info >= (3, ):
-    def u(s):
-        return s
-else:
-    def u(s):
-        if not isinstance(s, unicode):  # NOQA
-            s = unicode(s, "unicode_escape")  # NOQA
-        return s
-
-
-PROJECT_NAME = u("psutil")
-AUTHOR = u("Giampaolo Rodola'")
+PROJECT_NAME = "psutil"
+AUTHOR = "Giampaolo Rodola'"
 THIS_YEAR = str(datetime.datetime.now().year)
 HERE = os.path.abspath(os.path.dirname(__file__))
 
 
 def get_version():
     INIT = os.path.abspath(os.path.join(HERE, '../psutil/__init__.py'))
-    f = open(INIT, 'r')
-    try:
+    with open(INIT, 'r') as f:
         for line in f:
             if line.startswith('__version__'):
                 ret = eval(line.strip().split(' = ')[1])
                 assert ret.count('.') == 2, ret
                 for num in ret.split('.'):
                     assert num.isdigit(), ret
                 return ret
         else:
             raise ValueError("couldn't find version string")
-    finally:
-        f.close()
 
 VERSION = get_version()
 
 # If your documentation needs a minimal Sphinx version, state it here.
 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
@@ -72,17 +58,17 @@ source_suffix = '.rst'
 # The encoding of source files.
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
 master_doc = 'index'
 
 # General information about the project.
 project = PROJECT_NAME
-copyright = u('2009-%s, %s' % (THIS_YEAR, AUTHOR))
+copyright = '2009-%s, %s' % (THIS_YEAR, 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
 # built documents.
 #
 # The short X.Y version.
 version = VERSION
 
@@ -218,17 +204,17 @@ htmlhelp_basename = '%s-doc' % PROJECT_N
 # The font size ('10pt', '11pt' or '12pt').
 # latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass
 # [howto/manual]).
 latex_documents = [
     ('index', '%s.tex' % PROJECT_NAME,
-     u('%s documentation') % PROJECT_NAME, AUTHOR),
+     '%s documentation' % PROJECT_NAME, AUTHOR),
 ]
 
 # 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.
@@ -250,13 +236,13 @@ latex_documents = [
 # latex_domain_indices = True
 
 
 # -- 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, u('%s documentation') % PROJECT_NAME, [AUTHOR], 1)
+    ('index', PROJECT_NAME, '%s documentation' % PROJECT_NAME, [AUTHOR], 1)
 ]
 
 # If true, show URL addresses after external links.
 # man_show_urls = False
--- a/python/psutil/docs/index.rst
+++ b/python/psutil/docs/index.rst
@@ -13,18 +13,20 @@
 psutil documentation
 ====================
 
 Quick links
 -----------
 
 * `Home page <https://github.com/giampaolo/psutil>`__
 * `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>`__
-* `Forum <http://groups.google.com/group/psutil/topics>`__
+* `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+* `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
 * `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`__
 
 About
 -----
 
 From project's home page:
 
   psutil (python system and process utilities) is a cross-platform library for
@@ -33,18 +35,18 @@ From project's home page:
   **Python**.
   It is useful mainly for **system monitoring**, **profiling** and **limiting
   process resources** and **management of running processes**.
   It implements many functionalities offered by command line tools
   such as: *ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
   ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap*.
   It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**,
   both **32-bit** and **64-bit** architectures, with Python versions from
-  **2.4** to **3.4**.
-  `Pypy <http://pypy.org/>`__ is also known to work.
+  **2.6 to 3.4** (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.
 
 The psutil documentation you're reading is distributed as a single HTML page.
 
 System related functions
 ========================
 
 CPU
 ---
@@ -61,17 +63,17 @@ CPU
   - **nice** *(UNIX)*
   - **iowait** *(Linux)*
   - **irq** *(Linux, FreeBSD)*
   - **softirq** *(Linux)*
   - **steal** *(Linux 2.6.11+)*
   - **guest** *(Linux 2.6.24+)*
   - **guest_nice** *(Linux 3.2.0+)*
 
-  When *percpu* is ``True`` return a list of nameduples for each logical CPU
+  When *percpu* is ``True`` return a list of namedtuples for each logical CPU
   on the system.
   First element of the list refers to first CPU, second element to second CPU
   and so on.
   The order of the list is consistent across calls.
   Example output on Linux:
 
     >>> import psutil
     >>> psutil.cpu_times()
@@ -194,17 +196,17 @@ Memory
 .. function:: swap_memory()
 
   Return system swap memory statistics as a namedtuple including the following
   fields:
 
   * **total**: total swap memory in bytes
   * **used**: used swap memory in bytes
   * **free**: free swap memory in bytes
-  * **percent**: the percentage usage
+  * **percent**: the percentage usage calculated as ``(total - available) / total * 100``
   * **sin**: the number of bytes the system has swapped in from disk
     (cumulative)
   * **sout**: the number of bytes the system has swapped out from disk
     (cumulative)
 
   **sin** and **sout** on Windows are meaningless and are always set to ``0``.
   See `examples/meminfo.py <https://github.com/giampaolo/psutil/blob/master/examples/meminfo.py>`__
   script providing an example on how to convert bytes in a human readable form.
@@ -269,17 +271,17 @@ Disks
   - **write_count**: number of writes
   - **read_bytes**: number of bytes read
   - **write_bytes**: number of bytes written
   - **read_time**: time spent reading from disk (in milliseconds)
   - **write_time**: time spent writing to disk (in milliseconds)
 
   If *perdisk* is ``True`` return the same information for every physical disk
   installed on the system as a dictionary with partition names as the keys and
-  the namedutuple described above as the values.
+  the namedtuple described above as the values.
   See `examples/iotop.py <https://github.com/giampaolo/psutil/blob/master/examples/iotop.py>`__
   for an example application.
 
     >>> import psutil
     >>> psutil.disk_io_counters()
     sdiskio(read_count=8141, write_count=2431, read_bytes=290203, write_bytes=537676, read_time=5868, write_time=94922)
     >>>
     >>> psutil.disk_io_counters(perdisk=True)
@@ -316,17 +318,17 @@ Network
     snetio(bytes_sent=14508483, bytes_recv=62749361, packets_sent=84311, packets_recv=94888, errin=0, errout=0, dropin=0, dropout=0)
     >>>
     >>> psutil.net_io_counters(pernic=True)
     {'lo': snetio(bytes_sent=547971, bytes_recv=547971, packets_sent=5075, packets_recv=5075, errin=0, errout=0, dropin=0, dropout=0),
     'wlan0': snetio(bytes_sent=13921765, bytes_recv=62162574, packets_sent=79097, packets_recv=89648, errin=0, errout=0, dropin=0, dropout=0)}
 
 .. function:: net_connections(kind='inet')
 
-  Return system-wide socket connections as a list of namedutples.
+  Return system-wide socket connections as a list of namedtuples.
   Every namedtuple provides 7 attributes:
 
   - **fd**: the socket file descriptor, if retrievable, else ``-1``.
     If the connection refers to the current process this may be passed to
     `socket.fromfd() <http://docs.python.org/library/socket.html#socket.fromfd>`__
     to obtain a usable socket object.
   - **family**: the address family, either `AF_INET
     <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
@@ -378,34 +380,103 @@ Network
    +----------------+-----------------------------------------------------+
    | "udp6"         | UDP over IPv6                                       |
    +----------------+-----------------------------------------------------+
    | "unix"         | UNIX socket (both UDP and TCP protocols)            |
    +----------------+-----------------------------------------------------+
    | "all"          | the sum of all the possible families and protocols  |
    +----------------+-----------------------------------------------------+
 
+  On OSX 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/examples/netstat.py>`__.
   Example:
 
     >>> import psutil
     >>> psutil.net_connections()
-    [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
-     pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
-     pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
-     pconn(fd=-1, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+    [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
+     pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
+     pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
      ...]
 
   .. note:: (OSX) :class:`psutil.AccessDenied` is always raised unless running
      as root (lsof does the same).
   .. note:: (Solaris) UNIX sockets are not supported.
 
-  *New in 2.1.0*
+  .. versionadded:: 2.1.0
+
+.. function:: net_if_addrs()
+
+  Return the addresses associated to each NIC (network interface card)
+  installed on the system as a dictionary whose keys are the NIC names and
+  value is a list of namedtuples for each address assigned to the NIC.
+  Each namedtuple includes 4 fields:
+
+  - **family**
+  - **address**
+  - **netmask**
+  - **broadcast**
+
+  *family* can be either
+  `AF_INET <http://docs.python.org//library/socket.html#socket.AF_INET>`__,
+  `AF_INET6 <http://docs.python.org//library/socket.html#socket.AF_INET6>`__
+  or :const:`psutil.AF_LINK`, which refers to a MAC address.
+  *address* is the primary address, *netmask* and *broadcast* may be ``None``.
+  Example::
+
+    >>> import psutil
+    >>> psutil.net_if_addrs()
+    {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
+            snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
+            snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
+     'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
+               snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
+               snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+    >>>
+
+  See also `examples/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/examples/ifconfig.py>`__
+  for an example application.
+
+  .. note:: if you're interested in others families (e.g. AF_BLUETOOTH) you can
+    use the more powerful `netifaces <https://pypi.python.org/pypi/netifaces/>`__
+    extension.
+
+  .. note:: you can have more than one address of the same family associated
+    with each interface (that's why dict values are lists).
+
+  *New in 3.0.0*
+
+.. function:: net_if_stats()
+
+  Return information about each NIC (network interface card) installed on the
+  system as a dictionary whose keys are the NIC names and value is a namedtuple
+  with the following fields:
+
+  - **isup**
+  - **duplex**
+  - **speed**
+  - **mtu**
+
+  *isup* is a boolean indicating whether the NIC is up and running, *duplex*
+  can be either :const:`NIC_DUPLEX_FULL`, :const:`NIC_DUPLEX_HALF` or
+  :const:`NIC_DUPLEX_UNKNOWN`, *speed* is the NIC speed expressed in mega bits
+  (MB), if it can't be determined (e.g. 'localhost') it will be set to ``0``,
+  *mtu* is the maximum transmission unit expressed in bytes.
+  See also `examples/ifconfig.py <https://github.com/giampaolo/psutil/blob/master/examples/ifconfig.py>`__
+  for an example application.
+  Example:
+
+    >>> import psutil
+    >>> psutil.net_if_stats()
+    {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+     'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
+
+  *New in 3.0.0*
 
 
 Other system info
 -----------------
 
 .. function:: users()
 
   Return users currently connected on the system as a list of namedtuples
@@ -493,17 +564,17 @@ Functions
   - give them some time to terminate
   - send SIGKILL to those ones which are still alive
 
   Example::
 
     import psutil
 
     def on_terminate(proc):
-        print("process {} terminated".format(proc))
+        print("process {} terminated with exit code {}".format(proc, proc.returncode))
 
     procs = [...]  # a list of Process instances
     for p in procs:
         p.terminate()
     gone, alive = wait_procs(procs, timeout=3, callback=on_terminate)
     for p in alive:
         p.kill()
 
@@ -516,16 +587,28 @@ Exceptions
 
 .. class:: NoSuchProcess(pid, name=None, msg=None)
 
    Raised by :class:`Process` class methods when no process with the given
    *pid* is found in the current process list or when a process no longer
    exists. "name" is the name the process had before disappearing
    and gets set only if :meth:`Process.name()` was previosly called.
 
+.. class:: ZombieProcess(pid, name=None, ppid=None, msg=None)
+
+   This may be raised by :class:`Process` class methods when querying a zombie
+   process on UNIX (Windows doesn't have zombie processes). Depending on the
+   method called the OS may be able to succeed in retrieving the process
+   information or not.
+   Note: this is a subclass of :class:`NoSuchProcess` so if you're not
+   interested in retrieving zombies (e.g. when using :func:`process_iter()`)
+   you can ignore this exception and just catch :class:`NoSuchProcess`.
+
+   *New in 3.0.0*
+
 .. class:: AccessDenied(pid=None, name=None, msg=None)
 
     Raised by :class:`Process` class methods when permission to perform an
     action is denied. "name" is the name of the process (may be ``None``).
 
 .. class:: TimeoutExpired(seconds, pid=None, name=None, msg=None)
 
     Raised by :meth:`Process.wait` if timeout expires and process is still
@@ -604,30 +687,34 @@ Process class
 
         >>> import psutil, datetime
         >>> p = psutil.Process()
         >>> p.create_time()
         1307289803.47
         >>> datetime.datetime.fromtimestamp(p.create_time()).strftime("%Y-%m-%d %H:%M:%S")
         '2011-03-05 18:03:52'
 
-  .. method:: as_dict(attrs=[], ad_value=None)
+  .. method:: as_dict(attrs=None, ad_value=None)
 
      Utility method returning process information as a hashable dictionary.
      If *attrs* is specified it must be a list of strings reflecting available
      :class:`Process` class's attribute names (e.g. ``['cpu_times', 'name']``)
      else all public (read only) attributes are assumed. *ad_value* is the
      value which gets assigned to a dict key in case :class:`AccessDenied`
-     exception is raised when retrieving that particular process information.
+     or :class:`ZombieProcess` exception is raised when retrieving that
+     particular process information.
 
         >>> import psutil
         >>> p = psutil.Process()
         >>> p.as_dict(attrs=['pid', 'name', 'username'])
         {'username': 'giampaolo', 'pid': 12366, 'name': 'python'}
 
+     .. versionchanged:: 3.0.0 *ad_value* is used also when incurring into
+        :class:`ZombieProcess` exception, not only :class:`AccessDenied`
+
   .. method:: parent()
 
      Utility method which returns the parent process as a :class:`Process`
      object pre-emptively checking whether PID has been reused. If no parent
      PID is known return ``None``.
 
   .. method:: status()
 
@@ -641,26 +728,26 @@ Process class
   .. method:: username()
 
      The name of the user that owns the process. On UNIX this is calculated by
      using real process uid.
 
   .. method:: uids()
 
      The **real**, **effective** and **saved** user ids of this process as a
-     nameduple. This is the same as
+     namedtuple. This is the same as
      `os.getresuid() <http://docs.python.org//library/os.html#os.getresuid>`__
      but can be used for every process PID.
 
      Availability: UNIX
 
   .. method:: gids()
 
      The **real**, **effective** and **saved** group ids of this process as a
-     nameduple. This is the same as
+     namedtuple. This is the same as
      `os.getresgid() <http://docs.python.org//library/os.html#os.getresgid>`__
      but can be used for every process PID.
 
      Availability: UNIX
 
   .. method:: terminal()
 
      The terminal associated with this process, if any, else ``None``. This is
@@ -677,56 +764,61 @@ Process class
 
         >>> import psutil
         >>> p = psutil.Process()
         >>> p.nice(10)  # set
         >>> p.nice()  # get
         10
         >>>
 
+     Starting from `Python 3.3 <http://bugs.python.org/issue10784>`__ this
+     functionality is also available as
+     `os.getpriority() <http://docs.python.org/3/library/os.html#os.getpriority>`__
+     and
+     `os.setpriority() <http://docs.python.org/3/library/os.html#os.setpriority>`__
+     (UNIX only).
+
      On Windows this is available as well by using
      `GetPriorityClass <http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx>`__
      and `SetPriorityClass <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__
      and *value* is one of the
      :data:`psutil.*_PRIORITY_CLASS <psutil.ABOVE_NORMAL_PRIORITY_CLASS>`
      constants.
      Example which increases process priority on Windows:
 
         >>> p.nice(psutil.HIGH_PRIORITY_CLASS)
 
-     Starting from `Python 3.3 <http://bugs.python.org/issue10784>`__ this
-     same functionality is available as
-     `os.getpriority() <http://docs.python.org/3/library/os.html#os.getpriority>`__
-     and
-     `os.setpriority() <http://docs.python.org/3/library/os.html#os.setpriority>`__.
-
   .. method:: ionice(ioclass=None, value=None)
 
      Get or set
      `process I/O niceness <http://friedcpu.wordpress.com/2007/07/17/why-arent-you-using-ionice-yet/>`__ (priority).
      On Linux *ioclass* is one of the
      :data:`psutil.IOPRIO_CLASS_*<psutil.IOPRIO_CLASS_NONE>` constants.
      *value* is a number which goes from  ``0`` to ``7``. The higher the value,
      the lower the I/O priority of the process. On Windows only *ioclass* is
      used and it can be set to ``2`` (normal), ``1`` (low) or ``0`` (very low).
      The example below sets IDLE priority class for the current process,
      meaning it will only get I/O time when no other process needs the disk:
 
       >>> import psutil
       >>> p = psutil.Process()
       >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # set
       >>> p.ionice()  # get
-      pionice(ioclass=3, value=0)
+      pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
       >>>
 
      On Windows only *ioclass* is used and it can be set to ``2`` (normal),
      ``1`` (low) or ``0`` (very low).
 
      Availability: Linux and Windows > Vista
 
+     .. versionchanged:: 3.0.0 on >= Python 3.4 the returned ``ioclass``
+        constant is an `enum <https://docs.python.org/3/library/enum.html#module-enum>`__
+        instead of a plain integer.
+
   .. method:: rlimit(resource, limits=None)
 
      Get or set process resource limits (see
      `man prlimit <http://linux.die.net/man/2/prlimit>`__). *resource* is one of
      the :data:`psutil.RLIMIT_* <psutil.RLIMIT_INFINITY>` constants.
      *limits* is a ``(soft, hard)`` tuple.
      This is the same as `resource.getrlimit() <http://docs.python.org/library/resource.html#resource.getrlimit>`__
      and `resource.setrlimit() <http://docs.python.org/library/resource.html#resource.setrlimit>`__
@@ -756,17 +848,17 @@ Process class
      is returned for **read_bytes** and **write_bytes** fields. OSX is not
      supported.
 
       >>> import psutil
       >>> p = psutil.Process()
       >>> p.io_counters()
       pio(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0)
 
-     Availability: all platforms except OSX
+     Availability: all platforms except OSX and Solaris
 
   .. method:: num_ctx_switches()
 
      The number voluntary and involuntary context switches performed by
      this process.
 
   .. method:: num_fds()
 
@@ -831,28 +923,37 @@ Process class
         supposed to ignore.
 
   .. method:: cpu_affinity(cpus=None)
 
      Get or set process current
      `CPU affinity <http://www.linuxjournal.com/article/6799?page=0,0>`__.
      CPU affinity consists in telling the OS to run a certain process on a
      limited set of CPUs only. The number of eligible CPUs can be obtained with
-     ``list(range(psutil.cpu_count()))``.
+     ``list(range(psutil.cpu_count()))``. On set raises ``ValueError`` in case
+     an invalid CPU number is specified.
 
       >>> import psutil
       >>> psutil.cpu_count()
       4
       >>> p = psutil.Process()
       >>> p.cpu_affinity()  # get
       [0, 1, 2, 3]
       >>> p.cpu_affinity([0])  # set; from now on, process will run on CPU #0 only
+      >>> p.cpu_affinity()
+      [0]
+      >>>
+      >>> # reset affinity against all CPUs
+      >>> all_cpus = list(range(psutil.cpu_count()))
+      >>> p.cpu_affinity(all_cpus)
       >>>
 
-     Availability: Linux, Windows
+     Availability: Linux, Windows, BSD
+
+     .. versionchanged:: 2.2.0 added support for FreeBSD
 
   .. method:: memory_info()
 
      Return a tuple representing RSS (Resident Set Size) and VMS (Virtual
      Memory Size) in bytes. On UNIX *rss* and *vms* are the same values shown
      by ps. On Windows *rss* and *vms* refer to "Mem Usage" and "VM Size"
      columns of taskmgr.exe. For more detailed memory stats use
      :meth:`memory_info_ex`.
@@ -898,17 +999,17 @@ Process class
 
   .. method:: memory_percent()
 
      Compare physical system memory to process resident memory (RSS) and
      calculate process memory utilization as a percentage.
 
   .. method:: memory_maps(grouped=True)
 
-     Return process's mapped memory regions as a list of nameduples whose
+     Return process's mapped memory regions as a list of namedtuples whose
      fields are variable depending on the platform. As such, portable
      applications should rely on namedtuple's `path` and `rss` fields only.
      This method is useful to obtain a detailed representation of process
      memory usage as explained
      `here <http://bmaurer.blogspot.it/2006/03/memory-usage-with-smaps.html>`__.
      If *grouped* is ``True`` the mapped regions with the same *path* are
      grouped together and the different memory fields are summed.  If *grouped*
      is ``False`` every mapped region is shown as a single entity and the
@@ -953,25 +1054,41 @@ Process class
      returned either as the reference to process A is lost.
 
   .. method:: open_files()
 
      Return regular files opened by process as a list of namedtuples including
      the absolute file name and the file descriptor number (on Windows this is
      always ``-1``). Example:
 
-      >>> import psutil
-      >>> f = open('file.ext', 'w')
-      >>> p = psutil.Process()
-      >>> p.open_files()
-      [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)]
+     >>> import psutil
+     >>> f = open('file.ext', 'w')
+     >>> p = psutil.Process()
+     >>> p.open_files()
+     [popenfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)]
+
+     .. warning::
+       on Windows this is not fully reliable as due to some limitations of the
+       Windows API the underlying implementation may hang when retrieving
+       certain file handles.
+       In order to work around that psutil on Windows Vista (and higher) spawns
+       a thread and kills it if it's not responding after 100ms.
+       That implies that on Windows this method is not guaranteed to enumerate
+       all regular file handles (see full discusion
+       `here <https://github.com/giampaolo/psutil/pull/597>`_).
+
+     .. warning::
+       on FreeBSD this method can return files with a 'null' path (see
+       `issue 595 <https://github.com/giampaolo/psutil/pull/595>`_).
+
+     .. versionchanged:: 3.1.0 no longer hangs on Windows.
 
   .. method:: connections(kind="inet")
 
-    Return socket connections opened by process as a list of namedutples.
+    Return socket connections opened by process as a list of namedtuples.
     To get system-wide connections use :func:`psutil.net_connections()`.
     Every namedtuple provides 6 attributes:
 
     - **fd**: the socket file descriptor. This can be passed to
       `socket.fromfd() <http://docs.python.org/library/socket.html#socket.fromfd>`__
       to obtain a usable socket object.
       This is only available on UNIX; on Windows ``-1`` is always returned.
     - **family**: the address family, either `AF_INET
@@ -1027,20 +1144,20 @@ Process class
 
     Example:
 
       >>> import psutil
       >>> p = psutil.Process(1694)
       >>> p.name()
       'firefox'
       >>> p.connections()
-      [pconn(fd=115, family=2, type=1, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
-       pconn(fd=117, family=2, type=1, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
-       pconn(fd=119, family=2, type=1, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
-       pconn(fd=123, family=2, type=1, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+      [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
+       pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
+       pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
+       pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
 
   .. method:: is_running()
 
      Return whether the current process is running in the current process list.
      This is reliable also in case the process is gone and its PID reused by
      another process, therefore it must be preferred over doing
      ``psutil.pid_exists(p.pid)``.
 
@@ -1120,17 +1237,17 @@ Popen class
 
   .. note::
 
      Unlike `subprocess.Popen <http://docs.python.org/library/subprocess.html#subprocess.Popen>`__
      this class pre-emptively checks wheter PID has been reused on
      :meth:`send_signal() <psutil.Process.send_signal()>`,
      :meth:`terminate() <psutil.Process.terminate()>` and
      :meth:`kill() <psutil.Process.kill()>`
-     so that you don't accidentally terminate another process, fixing
+     so that you can't accidentally terminate another process, fixing
      http://bugs.python.org/issue6973.
 
   >>> import psutil
   >>> from subprocess import PIPE
   >>>
   >>> p = psutil.Popen(["/usr/bin/python", "-c", "print('hello')"], stdout=PIPE)
   >>> p.name()
   'python'
@@ -1192,16 +1309,20 @@ Constants
 
   A set of integers representing the priority of a process on Windows (see
   `MSDN documentation <http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx>`__).
   They can be used in conjunction with
   :meth:`psutil.Process.nice()` to get or set process priority.
 
   Availability: Windows
 
+  .. versionchanged:: 3.0.0 on Python >= 3.4 these constants are
+    `enums <https://docs.python.org/3/library/enum.html#module-enum>`__
+    instead of a plain integer.
+
 .. _const-ioprio:
 .. data:: IOPRIO_CLASS_NONE
           IOPRIO_CLASS_RT
           IOPRIO_CLASS_BE
           IOPRIO_CLASS_IDLE
 
   A set of integers representing the I/O priority of a process on Linux. They
   can be used in conjunction with :meth:`psutil.Process.ionice()` to get or set
@@ -1215,16 +1336,20 @@ Constants
   For further information refer to manuals of
   `ionice <http://linux.die.net/man/1/ionice>`__
   command line utility or
   `ioprio_get <http://linux.die.net/man/2/ioprio_get>`__
   system call.
 
   Availability: Linux
 
+  .. versionchanged:: 3.0.0 on Python >= 3.4 thse constants are
+    `enums <https://docs.python.org/3/library/enum.html#module-enum>`__
+    instead of a plain integer.
+
 .. _const-rlimit:
 .. data:: RLIMIT_INFINITY
           RLIMIT_AS
           RLIMIT_CORE
           RLIMIT_CPU
           RLIMIT_DATA
           RLIMIT_FSIZE
           RLIMIT_LOCKS
@@ -1240,8 +1365,36 @@ Constants
           RLIMIT_SIGPENDING
           RLIMIT_STACK
 
   Constants used for getting and setting process resource limits to be used in
   conjunction with :meth:`psutil.Process.rlimit()`. See
   `man prlimit <http://linux.die.net/man/2/prlimit>`__ for futher information.
 
   Availability: Linux
+
+.. _const-aflink:
+.. data:: AF_LINK
+
+  Constant which identifies a MAC address associated with a network interface.
+  To be used in conjunction with :func:`psutil.net_if_addrs()`.
+
+  *New in 3.0.0*
+
+.. _const-duplex:
+.. data:: NIC_DUPLEX_FULL
+          NIC_DUPLEX_HALF
+          NIC_DUPLEX_UNKNOWN
+
+  Constants which identifies whether a NIC (network interface card) has full or
+  half mode speed.  NIC_DUPLEX_FULL means the NIC is able to send and receive
+  data (files) simultaneously, NIC_DUPLEX_FULL means the NIC can either send or
+  receive data at a time.
+  To be used in conjunction with :func:`psutil.net_if_stats()`.
+
+  *New in 3.0.0*
+
+Development guide
+=================
+
+If you plan on hacking on psutil (e.g. want to add a new feature or fix a bug)
+take a look at the
+`development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_.
new file mode 100644
--- /dev/null
+++ b/python/psutil/docs/xxx
@@ -0,0 +1,11 @@
+cpu  1974613 1749 485728 6305758 80280 15 5924 0 0 0
+
+cpu0 519156 374 132999 5977865 72925 10 1458 0 0 0
+
+cpu1 524667 401 125931 108960 2110 4 2214 0 0 0
+
+cpu2 462286 520 117046 109514 2666 0 828 0 0 0
+
+cpu3 468502 453 109750 109418 2578 0 1424 0 0 0
+
+
--- a/python/psutil/examples/disk_usage.py
+++ b/python/psutil/examples/disk_usage.py
@@ -13,17 +13,16 @@ Device               Total     Used     
 /dev/sda6           345.9G    83.8G   244.5G    24%      ext4  /home
 /dev/sda1           296.0M    43.1M   252.9M    14%      vfat  /boot/efi
 /dev/sda2           600.0M   312.4M   287.6M    52%   fuseblk  /media/Recovery
 """
 
 import sys
 import os
 import psutil
-from psutil._compat import print_
 
 
 def bytes2human(n):
     # http://code.activestate.com/recipes/578019
     # >>> bytes2human(10000)
     # '9.8K'
     # >>> bytes2human(100001221)
     # '95.4M'
@@ -35,27 +34,27 @@ def bytes2human(n):
         if n >= prefix[s]:
             value = float(n) / prefix[s]
             return '%.1f%s' % (value, s)
     return "%sB" % n
 
 
 def main():
     templ = "%-17s %8s %8s %8s %5s%% %9s  %s"
-    print_(templ % ("Device", "Total", "Used", "Free", "Use ", "Type",
-                    "Mount"))
+    print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type",
+                   "Mount"))
     for part in psutil.disk_partitions(all=False):
         if os.name == 'nt':
             if 'cdrom' in part.opts or part.fstype == '':
                 # skip cd-rom drives with no disk in it; they may raise
                 # ENOENT, pop-up a Windows GUI error for a non-ready
                 # partition or just hang.
                 continue
         usage = psutil.disk_usage(part.mountpoint)
-        print_(templ % (
+        print(templ % (
             part.device,
             bytes2human(usage.total),
             bytes2human(usage.used),
             bytes2human(usage.free),
             int(usage.percent),
             part.fstype,
             part.mountpoint))
 
--- a/python/psutil/examples/free.py
+++ b/python/psutil/examples/free.py
@@ -9,33 +9,32 @@ A clone of 'free' cmdline utility.
 
 $ python examples/free.py
              total       used       free     shared    buffers      cache
 Mem:      10125520    8625996    1499524          0     349500    3307836
 Swap:            0          0          0
 """
 
 import psutil
-from psutil._compat import print_
 
 
 def main():
     virt = psutil.virtual_memory()
     swap = psutil.swap_memory()
     templ = "%-7s %10s %10s %10s %10s %10s %10s"
-    print_(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache'))
-    print_(templ % (
+    print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache'))
+    print(templ % (
         'Mem:',
         int(virt.total / 1024),
         int(virt.used / 1024),
         int(virt.free / 1024),
         int(getattr(virt, 'shared', 0) / 1024),
         int(getattr(virt, 'buffers', 0) / 1024),
         int(getattr(virt, 'cached', 0) / 1024)))
-    print_(templ % (
+    print(templ % (
         'Swap:', int(swap.total / 1024),
         int(swap.used / 1024),
         int(swap.free / 1024),
         '',
         '',
         ''))
 
 if __name__ == '__main__':
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/ifconfig.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of 'ifconfig' on UNIX.
+
+$ python examples/ifconfig.py
+lo (speed=0MB, duplex=?, mtu=65536, up=yes):
+    IPv4     address   : 127.0.0.1
+             broadcast : 127.0.0.1
+             netmask   : 255.0.0.0
+    IPv6     address   : ::1
+             netmask   : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+    MAC      address   : 00:00:00:00:00:00
+             broadcast : 00:00:00:00:00:00
+
+wlan0 (speed=0MB, duplex=?, mtu=1500, up=yes):
+    IPv4     address   : 10.0.3.1
+             broadcast : 10.0.3.255
+             netmask   : 255.255.255.0
+    IPv6     address   : fe80::3005:adff:fe31:8698
+             netmask   : ffff:ffff:ffff:ffff::
+    MAC      address   : 32:05:ad:31:86:98
+             broadcast : ff:ff:ff:ff:ff:ff
+
+eth0 (speed=100MB, duplex=full, mtu=1500, up=yes):
+    IPv4     address   : 192.168.1.2
+             broadcast : 192.168.1.255
+             netmask   : 255.255.255.0
+    IPv6     address   : fe80::c685:8ff:fe45:641
+             netmask   : ffff:ffff:ffff:ffff::
+    MAC      address   : c4:85:08:45:06:41
+             broadcast : ff:ff:ff:ff:ff:ff
+"""
+
+from __future__ import print_function
+import socket
+
+import psutil
+
+
+af_map = {
+    socket.AF_INET: 'IPv4',
+    socket.AF_INET6: 'IPv6',
+    psutil.AF_LINK: 'MAC',
+}
+
+duplex_map = {
+    psutil.NIC_DUPLEX_FULL: "full",
+    psutil.NIC_DUPLEX_HALF: "half",
+    psutil.NIC_DUPLEX_UNKNOWN: "?",
+}
+
+
+def main():
+    stats = psutil.net_if_stats()
+    for nic, addrs in psutil.net_if_addrs().items():
+        if nic in stats:
+            print("%s (speed=%sMB, duplex=%s, mtu=%s, up=%s):" % (
+                nic, stats[nic].speed, duplex_map[stats[nic].duplex],
+                stats[nic].mtu, "yes" if stats[nic].isup else "no"))
+        else:
+            print("%s:" % (nic))
+        for addr in addrs:
+            print("    %-8s" % af_map.get(addr.family, addr.family), end="")
+            print(" address   : %s" % addr.address)
+            if addr.broadcast:
+                print("             broadcast : %s" % addr.broadcast)
+            if addr.netmask:
+                print("             netmask   : %s" % addr.netmask)
+        print("")
+
+
+if __name__ == '__main__':
+    main()
--- a/python/psutil/examples/iotop.py
+++ b/python/psutil/examples/iotop.py
@@ -25,24 +25,25 @@ 3831  giampao    0.00 B/s    0.00 B/s  /
 3841  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi-bus-launcher
 3845  giampao    0.00 B/s    0.00 B/s  /bin/dbus-daemon
 3848  giampao    0.00 B/s    0.00 B/s  /usr/lib/at-spi2-core/at-spi2-registryd
 3862  giampao    0.00 B/s    0.00 B/s  /usr/lib/gnome-settings-daemon
 
 Author: Giampaolo Rodola' <g.rodola@gmail.com>
 """
 
-import os
+import atexit
+import time
 import sys
+try:
+    import curses
+except ImportError:
+    sys.exit('platform not supported')
+
 import psutil
-if not hasattr(psutil.Process, 'io_counters') or os.name != 'posix':
-    sys.exit('platform not supported')
-import time
-import curses
-import atexit
 
 
 # --- curses stuff
 def tear_down():
     win.keypad(0)
     curses.nocbreak()
     curses.echo()
     curses.endwin()
@@ -111,17 +112,17 @@ def poll(interval):
     # then retrieve the same info again
     for p in procs[:]:
         try:
             p._after = p.io_counters()
             p._cmdline = ' '.join(p.cmdline())
             if not p._cmdline:
                 p._cmdline = p.name()
             p._username = p.username()
-        except psutil.NoSuchProcess:
+        except (psutil.NoSuchProcess, psutil.ZombieProcess):
             procs.remove(p)
     disks_after = psutil.disk_io_counters()
 
     # finally calculate results by comparing data before and
     # after the interval
     for p in procs:
         p._read_per_sec = p._after.read_bytes - p._before.read_bytes
         p._write_per_sec = p._after.write_bytes - p._before.write_bytes
@@ -162,17 +163,17 @@ def refresh_window(procs, disks_read, di
         except curses.error:
             break
     win.refresh()
 
 
 def main():
     try:
         interval = 0
-        while 1:
+        while True:
             args = poll(interval)
             refresh_window(*args)
             interval = 1
     except (KeyboardInterrupt, SystemExit):
         pass
 
 if __name__ == '__main__':
     main()
--- a/python/psutil/examples/meminfo.py
+++ b/python/psutil/examples/meminfo.py
@@ -26,17 +26,16 @@ Total      :      0B
 Used       :      0B
 Free       :      0B
 Percent    :     0.0
 Sin        :      0B
 Sout       :      0B
 """
 
 import psutil
-from psutil._compat import print_
 
 
 def bytes2human(n):
     # http://code.activestate.com/recipes/578019
     # >>> bytes2human(10000)
     # '9.8K'
     # >>> bytes2human(100001221)
     # '95.4M'
@@ -51,19 +50,19 @@ def bytes2human(n):
     return "%sB" % n
 
 
 def pprint_ntuple(nt):
     for name in nt._fields:
         value = getattr(nt, name)
         if name != 'percent':
             value = bytes2human(value)
-        print_('%-10s : %7s' % (name.capitalize(), value))
+        print('%-10s : %7s' % (name.capitalize(), value))
 
 
 def main():
-    print_('MEMORY\n------')
+    print('MEMORY\n------')
     pprint_ntuple(psutil.virtual_memory())
-    print_('\nSWAP\n----')
+    print('\nSWAP\n----')
     pprint_ntuple(psutil.swap_memory())
 
 if __name__ == '__main__':
     main()
--- a/python/psutil/examples/netstat.py
+++ b/python/psutil/examples/netstat.py
@@ -18,46 +18,45 @@ tcp   172.17.42.1:49102  127.0.0.1:19305
 tcp   172.17.42.1:55797  127.0.0.1:443    CLOSE_WAIT    13651  GoogleTalkPlugi
 ...
 """
 
 import socket
 from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
 
 import psutil
-from psutil._compat import print_
 
 
 AD = "-"
 AF_INET6 = getattr(socket, 'AF_INET6', object())
 proto_map = {
     (AF_INET, SOCK_STREAM): 'tcp',
     (AF_INET6, SOCK_STREAM): 'tcp6',
     (AF_INET, SOCK_DGRAM): 'udp',
     (AF_INET6, SOCK_DGRAM): 'udp6',
 }
 
 
 def main():
     templ = "%-5s %-30s %-30s %-13s %-6s %s"
-    print_(templ % (
+    print(templ % (
         "Proto", "Local address", "Remote address", "Status", "PID",
         "Program name"))
     proc_names = {}
     for p in psutil.process_iter():
         try:
             proc_names[p.pid] = p.name()
         except psutil.Error:
             pass
     for c in psutil.net_connections(kind='inet'):
         laddr = "%s:%s" % (c.laddr)
         raddr = ""
         if c.raddr:
             raddr = "%s:%s" % (c.raddr)
-        print_(templ % (
+        print(templ % (
             proto_map[(c.family, c.type)],
             laddr,
             raddr or AD,
             c.status,
             c.pid or AD,
             proc_names.get(c.pid, '?')[:15],
         ))
 
--- a/python/psutil/examples/nettop.py
+++ b/python/psutil/examples/nettop.py
@@ -26,23 +26,23 @@ pkts-recv               6753724         
 eth1                      TOTAL         PER-SEC
 -----------------------------------------------------------
 bytes-sent             131.77 M        0.00 B/s
 bytes-recv               1.28 G        0.00 B/s
 pkts-sent                     0               0
 pkts-recv               1214470               0
 """
 
+import atexit
+import time
 import sys
-import os
-if os.name != 'posix':
+try:
+    import curses
+except ImportError:
     sys.exit('platform not supported')
-import atexit
-import curses
-import time
 
 import psutil
 
 
 # --- curses stuff
 def tear_down():
     win.keypad(0)
     curses.nocbreak()
new file mode 100755
--- /dev/null
+++ b/python/psutil/examples/pidof.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola', karthikrev. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""
+A clone of 'pidof' cmdline utility.
+$ pidof python
+1140 1138 1136 1134 1133 1129 1127 1125 1121 1120 1119
+"""
+
+from __future__ import print_function
+import psutil
+import sys
+
+
+def pidof(pgname):
+    pids = []
+    for proc in psutil.process_iter():
+        # search for matches in the process name and cmdline
+        try:
+            name = proc.name()
+        except psutil.Error:
+            pass
+        else:
+            if name == pgname:
+                pids.append(str(proc.pid))
+                continue
+
+        try:
+            cmdline = proc.cmdline()
+        except psutil.Error:
+            pass
+        else:
+            if cmdline and cmdline[0] == pgname:
+                pids.append(str(proc.pid))
+
+    return pids
+
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: %s pgname' % __file__)
+    else:
+        pgname = sys.argv[1]
+    pids = pidof(pgname)
+    if pids:
+        print(" ".join(pids))
+
+if __name__ == '__main__':
+    main()
--- a/python/psutil/examples/pmap.py
+++ b/python/psutil/examples/pmap.py
@@ -28,31 +28,30 @@ 00007fff94be6000        48K  rw-p    [st
 00007fff94dd1000         4K  r-xp    [vdso]
 ffffffffff600000         0K  r-xp    [vsyscall]
 ...
 """
 
 import sys
 
 import psutil
-from psutil._compat import print_
 
 
 def main():
     if len(sys.argv) != 2:
         sys.exit('usage: pmap <pid>')
     p = psutil.Process(int(sys.argv[1]))
-    print_("pid=%s, name=%s" % (p.pid, p.name()))
+    print("pid=%s, name=%s" % (p.pid, p.name()))
     templ = "%-16s %10s  %-7s %s"
-    print_(templ % ("Address", "RSS", "Mode", "Mapping"))
+    print(templ % ("Address", "RSS", "Mode", "Mapping"))
     total_rss = 0
     for m in p.memory_maps(grouped=False):
         total_rss += m.rss
-        print_(templ % (
+        print(templ % (
             m.addr.split('-')[0].zfill(16),
             str(m.rss / 1024) + 'K',
             m.perms,
             m.path))
-    print_("-" * 33)
-    print_(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
+    print("-" * 33)
+    print(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
 
 if __name__ == '__main__':
     main()
--- a/python/psutil/examples/process_detail.py
+++ b/python/psutil/examples/process_detail.py
@@ -63,51 +63,56 @@ def print_(a, b):
     sys.stdout.flush()
 
 
 def run(pid):
     ACCESS_DENIED = ''
     try:
         p = psutil.Process(pid)
         pinfo = p.as_dict(ad_value=ACCESS_DENIED)
-    except psutil.NoSuchProcess:
-        sys.exit(str(sys.exc_info()[1]))
+    except psutil.NoSuchProcess as err:
+        sys.exit(str(err))
 
     try:
         parent = p.parent()
         if parent:
             parent = '(%s)' % parent.name()
         else:
             parent = ''
     except psutil.Error:
         parent = ''
-    started = datetime.datetime.fromtimestamp(
-        pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
+    if pinfo['create_time'] != ACCESS_DENIED:
+        started = datetime.datetime.fromtimestamp(
+            pinfo['create_time']).strftime('%Y-%m-%d %H:%M')
+    else:
+        started = ACCESS_DENIED
     io = pinfo.get('io_counters', ACCESS_DENIED)
-    mem = '%s%% (resident=%s, virtual=%s) ' % (
-        round(pinfo['memory_percent'], 1),
-        convert_bytes(pinfo['memory_info'].rss),
-        convert_bytes(pinfo['memory_info'].vms))
+    if pinfo['memory_info'] != ACCESS_DENIED:
+        mem = '%s%% (resident=%s, virtual=%s) ' % (
+            round(pinfo['memory_percent'], 1),
+            convert_bytes(pinfo['memory_info'].rss),
+            convert_bytes(pinfo['memory_info'].vms))
+    else:
+        mem = ACCESS_DENIED
     children = p.children()
 
     print_('pid', pinfo['pid'])
     print_('name', pinfo['name'])
     print_('exe', pinfo['exe'])
     print_('parent', '%s %s' % (pinfo['ppid'], parent))
     print_('cmdline', ' '.join(pinfo['cmdline']))
     print_('started', started)
     print_('user', pinfo['username'])
     if POSIX and pinfo['uids'] and pinfo['gids']:
         print_('uids', 'real=%s, effective=%s, saved=%s' % pinfo['uids'])
     if POSIX and pinfo['gids']:
         print_('gids', 'real=%s, effective=%s, saved=%s' % pinfo['gids'])
     if POSIX:
         print_('terminal', pinfo['terminal'] or '')
-    if hasattr(p, 'getcwd'):
-        print_('cwd', pinfo['cwd'])
+    print_('cwd', pinfo['cwd'])
     print_('memory', mem)
     print_('cpu', '%s%% (user=%s, system=%s)' % (
         pinfo['cpu_percent'],
         getattr(pinfo['cpu_times'], 'user', '?'),
         getattr(pinfo['cpu_times'], 'system', '?')))
     print_('status', pinfo['status'])
     print_('niceness', pinfo['nice'])
     print_('num threads', pinfo['num_threads'])
--- a/python/psutil/examples/ps.py
+++ b/python/psutil/examples/ps.py
@@ -11,29 +11,28 @@ A clone of 'ps -aux' on UNIX.
 ...
 """
 
 import datetime
 import os
 import time
 
 import psutil
-from psutil._compat import print_
 
 
 def main():
     today_day = datetime.date.today()
     templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s  %s"
     attrs = ['pid', 'cpu_percent', 'memory_percent', 'name', 'cpu_times',
              'create_time', 'memory_info']
     if os.name == 'posix':
         attrs.append('uids')
         attrs.append('terminal')
-    print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
-                    "START", "TIME", "COMMAND"))
+    print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
+                   "START", "TIME", "COMMAND"))
     for p in psutil.process_iter():
         try:
             pinfo = p.as_dict(attrs, ad_value='')
         except psutil.NoSuchProcess:
             pass
         else:
             if pinfo['create_time']:
                 ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
@@ -60,22 +59,23 @@ def main():
             if os.name == 'nt' and '\\' in user:
                 user = user.split('\\')[1]
             vms = pinfo['memory_info'] and \
                 int(pinfo['memory_info'].vms / 1024) or '?'
             rss = pinfo['memory_info'] and \
                 int(pinfo['memory_info'].rss / 1024) or '?'
             memp = pinfo['memory_percent'] and \
                 round(pinfo['memory_percent'], 1) or '?'
-            print_(templ % (user[:10],
-                            pinfo['pid'],
-                            pinfo['cpu_percent'],
-                            memp,
-                            vms,
-                            rss,
-                            pinfo.get('terminal', '') or '?',
-                            ctime,
-                            cputime,
-                            pinfo['name'].strip() or '?'))
+            print(templ % (
+                user[:10],
+                pinfo['pid'],
+                pinfo['cpu_percent'],
+                memp,
+                vms,
+                rss,
+                pinfo.get('terminal', '') or '?',
+                ctime,
+                cputime,
+                pinfo['name'].strip() or '?'))
 
 
 if __name__ == '__main__':
     main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/pstree.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Similar to 'ps aux --forest' on Linux, prints the process list
+as a tree structure.
+
+$ python examples/pstree.py
+0 ?
+|- 1 init
+| |- 289 cgmanager
+| |- 616 upstart-socket-bridge
+| |- 628 rpcbind
+| |- 892 upstart-file-bridge
+| |- 907 dbus-daemon
+| |- 978 avahi-daemon
+| | `_ 979 avahi-daemon
+| |- 987 NetworkManager
+| | |- 2242 dnsmasq
+| | `_ 10699 dhclient
+| |- 993 polkitd
+| |- 1061 getty
+| |- 1066 su
+| | `_ 1190 salt-minion...
+...
+"""
+
+from __future__ import print_function
+import collections
+import sys
+
+import psutil
+
+
+def print_tree(parent, tree, indent=''):
+    try:
+        name = psutil.Process(parent).name()
+    except psutil.Error:
+        name = "?"
+    print(parent, name)
+    if parent not in tree:
+        return
+    children = tree[parent][:-1]
+    for child in children:
+        sys.stdout.write(indent + "|- ")
+        print_tree(child, tree, indent + "| ")
+    child = tree[parent][-1]
+    sys.stdout.write(indent + "`_ ")
+    print_tree(child, tree, indent + "  ")
+
+
+def main():
+    # construct a dict where 'values' are all the processes
+    # having 'key' as their parent
+    tree = collections.defaultdict(list)
+    for p in psutil.process_iter():
+        try:
+            tree[p.ppid()].append(p.pid)
+        except (psutil.NoSuchProcess, psutil.ZombieProcess):
+            pass
+    # on systems supporting PID 0, PID 0's parent is usually 0
+    if 0 in tree and 0 in tree[0]:
+        tree[0].remove(0)
+    print_tree(min(tree), tree)
+
+
+if __name__ == '__main__':
+    main()
--- a/python/psutil/examples/top.py
+++ b/python/psutil/examples/top.py
@@ -29,24 +29,25 @@ 3936   giampaol    0    1G  111M    2.8 
 4401   giampaol    0  536M  141M    2.8  1.4  35:42.73  skype
 4047   giampaol    0  743M   76M    1.8  0.8  42:03.33  unity-panel-service
 13155  giampaol    0    1G  280M    1.8  2.8  41:57.34  chrome
 10     root        0    0B    0B    0.9  0.0   4:01.81  rcu_sched
 339    giampaol    0    1G  113M    0.9  1.1   8:15.73  chrome
 ...
 """
 
+from datetime import datetime, timedelta
+import atexit
 import os
+import time
 import sys
-if os.name != 'posix':
+try:
+    import curses
+except ImportError:
     sys.exit('platform not supported')
-import atexit
-import curses
-import time
-from datetime import datetime, timedelta
 
 import psutil
 
 
 # --- curses stuff
 def tear_down():
     win.keypad(0)
     curses.nocbreak()
@@ -216,17 +217,17 @@ def refresh_window(procs, procs_status):
         except curses.error:
             break
         win.refresh()
 
 
 def main():
     try:
         interval = 0
-        while 1:
+        while True:
             args = poll(interval)
             refresh_window(*args)
             interval = 1
     except (KeyboardInterrupt, SystemExit):
         pass
 
 if __name__ == '__main__':
     main()
--- a/python/psutil/examples/who.py
+++ b/python/psutil/examples/who.py
@@ -13,22 +13,21 @@ giampaolo       tty7            2014-02-
 giampaolo       pts/7           2014-02-24 18:25  (:192.168.1.56)
 giampaolo       pts/8           2014-02-24 18:25  (:0)
 giampaolo       pts/9           2014-02-27 01:32  (:0)
 """
 
 from datetime import datetime
 
 import psutil
-from psutil._compat import print_
 
 
 def main():
     users = psutil.users()
     for user in users:
-        print_("%-15s %-15s %s  (%s)" % (
+        print("%-15s %-15s %s  (%s)" % (
             user.name,
             user.terminal or '-',
             datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"),
             user.host))
 
 if __name__ == '__main__':
     main()
--- a/python/psutil/make.bat
+++ b/python/psutil/make.bat
@@ -2,229 +2,200 @@
 
 rem ==========================================================================
 rem Shortcuts for various tasks, emulating UNIX "make" on Windows.
 rem It is primarly intended as a shortcut for compiling / installing
 rem psutil ("make.bat build", "make.bat install") and running tests
 rem ("make.bat test").
 rem
 rem This script is modeled after my Windows installation which uses:
-rem - mingw32 for Python 2.4 and 2.5
 rem - Visual studio 2008 for Python 2.6, 2.7, 3.2
 rem - Visual studio 2010 for Python 3.3+
 rem ...therefore it might not work on your Windows installation.
 rem
 rem By default C:\Python27\python.exe is used.
 rem To compile for a specific Python version run:
-rem
-rem     set PYTHON=C:\Python24\python.exe & make.bat build
+rem     set PYTHON=C:\Python34\python.exe & make.bat build
 rem
-rem If you compile by using mingw on Python 2.4 and 2.5 you need to patch
-rem distutils first: http://stackoverflow.com/questions/13592192
+rem To use a different test script:
+rem      set PYTHON=C:\Python34\python.exe & set TSCRIPT=foo.py & make.bat test
 rem ==========================================================================
 
 if "%PYTHON%" == "" (
     set PYTHON=C:\Python27\python.exe
 )
 if "%TSCRIPT%" == "" (
     set TSCRIPT=test\test_psutil.py
 )
 
-rem Needed to compile using Mingw.
-set PATH=C:\MinGW\bin;%PATH%
+set PYTHON26=C:\Python26\python.exe
+set PYTHON27=C:\Python27\python.exe
+set PYTHON33=C:\Python33\python.exe
+set PYTHON34=C:\Python34\python.exe
+set PYTHON26-64=C:\Python26-64\python.exe
+set PYTHON27-64=C:\Python27-64\python.exe
+set PYTHON33-64=C:\Python33-64\python.exe
+set PYTHON34-64=C:\Python34-64\python.exe
+
+set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64%
 
 rem Needed to locate the .pypirc file and upload exes on PYPI.
 set HOME=%USERPROFILE%
 
 rem ==========================================================================
 
 if "%1" == "help" (
     :help
     echo Run `make ^<target^>` where ^<target^> is one of:
     echo   build         compile without installing
-    echo   build-exes    create exe installers in dist directory
-    echo   build-wheels  create wheel installers in dist directory
+    echo   build-all     build exes + wheels
     echo   clean         clean build files
+    echo   flake8        run flake8
     echo   install       compile and install
-    echo   memtest       run memory leak tests
-    echo   setup-env     install pip, unittest2, wheels for all python versions
+    echo   setup-dev-env install pip, pywin32, wheels, etc. for all python versions
     echo   test          run tests
+    echo   test-memleaks run memory leak tests
     echo   test-process  run process related tests
     echo   test-system   run system APIs related tests
     echo   uninstall     uninstall
-    echo   upload-exes   upload exe installers on pypi
-    echo   upload-wheels upload wheel installers on pypi
+    echo   upload-all    upload exes + wheels
     goto :eof
 )
 
 if "%1" == "clean" (
-    :clean
     for /r %%R in (__pycache__) do if exist %%R (rmdir /S /Q %%R)
     for /r %%R in (*.pyc) do if exist %%R (del /s %%R)
     for /r %%R in (*.pyd) do if exist %%R (del /s %%R)
     for /r %%R in (*.orig) do if exist %%R (del /s %%R)
     for /r %%R in (*.bak) do if exist %%R (del /s %%R)
     for /r %%R in (*.rej) do if exist %%R (del /s %%R)
     if exist psutil.egg-info (rmdir /S /Q psutil.egg-info)
     if exist build (rmdir /S /Q build)
     if exist dist (rmdir /S /Q dist)
     goto :eof
 )
 
 if "%1" == "build" (
     :build
-    if %PYTHON%==C:\Python24\python.exe (
-        %PYTHON% setup.py build -c mingw32
-    ) else if %PYTHON%==C:\Python25\python.exe (
-        %PYTHON% setup.py build -c mingw32
-    ) else (
-        %PYTHON% setup.py build
-    )
+    "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
+    %PYTHON% setup.py build
+    if %errorlevel% neq 0 goto :error
+	rem copies *.pyd files in ./psutil directory in order to allow
+	rem "import psutil" when using the interactive interpreter from
+    rem within this directory.
+    %PYTHON% setup.py build_ext -i
     if %errorlevel% neq 0 goto :error
     goto :eof
 )
 
 if "%1" == "install" (
     :install
-    if %PYTHON%==C:\Python24\python.exe (
-        %PYTHON% setup.py build -c mingw32 install
-    ) else if %PYTHON%==C:\Python25\python.exe (
-        %PYTHON% setup.py build -c mingw32 install
-    ) else (
-        %PYTHON% setup.py build install
-    )
+    call :build
+    %PYTHON% setup.py install
     goto :eof
 )
 
 if "%1" == "uninstall" (
-    :uninstall
     for %%A in ("%PYTHON%") do (
         set folder=%%~dpA
     )
     for /F "delims=" %%i in ('dir /b %folder%\Lib\site-packages\*psutil*') do (
         rmdir /S /Q %folder%\Lib\site-packages\%%i
     )
     goto :eof
 )
 
 if "%1" == "test" (
-    :test
     call :install
     %PYTHON% %TSCRIPT%
     goto :eof
 )
 
 if "%1" == "test-process" (
-    :test
     call :install
     %PYTHON% -m unittest -v test.test_psutil.TestProcess
     goto :eof
 )
 
 if "%1" == "test-system" (
-    :test
     call :install
     %PYTHON% -m unittest -v test.test_psutil.TestSystem
     goto :eof
 )
 
 if "%1" == "test-memleaks" (
-    :memtest
     call :install
     %PYTHON% test\test_memory_leaks.py
     goto :eof
 )
 
-if "%1" == "build-exes" (
-    :build-exes
-    rem mingw 32 versions
-    C:\Python24\python.exe setup.py build -c mingw32 bdist_wininst || goto :error
-    C:\Python25\python.exe setup.py build -c mingw32 bdist_wininst || goto :error
-    rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+)
-    C:\Python26\python.exe setup.py build bdist_wininst || goto :error
-    C:\Python27\python.exe setup.py build bdist_wininst || goto :error
-    C:\Python33\python.exe setup.py build bdist_wininst || goto :error
-    C:\Python34\python.exe setup.py build bdist_wininst || goto :error
-    rem 64 bit versions
-    rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first:
-    rem http://stackoverflow.com/questions/11072521/
-    rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh)
+if "%1" == "build-all" (
+    :build-all
     "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    C:\Python27-64\python.exe setup.py build bdist_wininst || goto :error
-    C:\Python33-64\python.exe setup.py build bdist_wininst || goto :error
-    C:\Python34-64\python.exe setup.py build bdist_wininst || goto :error
-    echo OK
-    goto :eof
-)
-
-if "%1" == "upload-exes" (
-    :upload-exes
-    rem mingw 32 versions
-    C:\Python25\python.exe setup.py build -c mingw32 bdist_wininst upload || goto :error
-    rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+)
-    C:\Python26\python.exe setup.py bdist_wininst upload || goto :error
-    C:\Python27\python.exe setup.py bdist_wininst upload || goto :error
-    C:\Python33\python.exe setup.py bdist_wininst upload || goto :error
-    C:\Python34\python.exe setup.py bdist_wininst upload || goto :error
-    rem 64 bit versions
-    C:\Python27-64\python.exe setup.py build bdist_wininst upload || goto :error
-    C:\Python33-64\python.exe setup.py build bdist_wininst upload || goto :error
-    C:\Python34-64\python.exe setup.py build bdist_wininst upload || goto :error
+    for %%P in (%ALL_PYTHONS%) do (
+        @echo ------------------------------------------------
+        @echo building exe for %%P
+        @echo ------------------------------------------------
+        %%P setup.py build bdist_wininst || goto :error
+        @echo ------------------------------------------------
+        @echo building wheel for %%P
+        @echo ------------------------------------------------
+        %%P setup.py build bdist_wheel || goto :error
+    )
     echo OK
     goto :eof
 )
 
-if "%1" == "setup-env" (
-    :setup-env
-    C:\python27\python.exe -c "import urllib2; url = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); data = url.read(); f = open('get-pip.py', 'w'); f.write(data)"
-    C:\python26\python.exe get-pip.py & C:\python26\scripts\pip install unittest2 wheel --upgrade
-    C:\python27\python.exe get-pip.py & C:\python27\scripts\pip install wheel --upgrade
-    C:\python33\python.exe get-pip.py & C:\python33\scripts\pip install wheel --upgrade
-    C:\python34\scripts\easy_install.exe wheel
-    rem 64-bit versions
-    C:\python27-64\python.exe get-pip.py & C:\python27-64\scripts\pip install wheel --upgrade
-    C:\python33-64\python.exe get-pip.py & C:\python33-64\scripts\pip install wheel --upgrade
-    C:\python34-64\scripts\easy_install.exe wheel
-    goto :eof
-)
-
-if "%1" == "build-wheels" (
-    :build-wheels
-    C:\Python26\python.exe setup.py build bdist_wheel || goto :error
-    C:\Python27\python.exe setup.py build bdist_wheel || goto :error
-    C:\Python33\python.exe setup.py build bdist_wheel || goto :error
-    C:\Python34\python.exe setup.py build bdist_wheel || goto :error
-    rem 64 bit versions
-    rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first:
-    rem http://stackoverflow.com/questions/11072521/
-    rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh)
+if "%1" == "upload-all" (
+    :upload-exes
     "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    C:\Python27-64\python.exe setup.py build bdist_wheel || goto :error
-    C:\Python33-64\python.exe setup.py build bdist_wheel || goto :error
-    C:\Python34-64\python.exe setup.py build bdist_wheel || goto :error
+    for %%P in (%ALL_PYTHONS%) do (
+        @echo ------------------------------------------------
+        @echo uploading exe for %%P
+        @echo ------------------------------------------------
+        %%P setup.py build bdist_wininst upload || goto :error
+        @echo ------------------------------------------------
+        @echo uploading wheel for %%P
+        @echo ------------------------------------------------
+        %%P setup.py build bdist_wheel upload || goto :error
+    )
     echo OK
     goto :eof
 )
 
-if "%1" == "upload-wheels" (
-    :build-wheels
-    C:\Python26\python.exe setup.py build bdist_wheel upload || goto :error
-    C:\Python27\python.exe setup.py build bdist_wheel upload || goto :error
-    C:\Python33\python.exe setup.py build bdist_wheel upload || goto :error
-    C:\Python34\python.exe setup.py build bdist_wheel upload || goto :error
-    rem 64 bit versions
-    rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first:
-    rem http://stackoverflow.com/questions/11072521/
-    rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh)
-    "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat"
-    C:\Python27-64\python.exe setup.py build bdist_wheel upload || goto :error
-    C:\Python33-64\python.exe setup.py build bdist_wheel upload || goto :error
-    C:\Python34-64\python.exe setup.py build bdist_wheel upload || goto :error
-    echo OK
+if "%1" == "setup-dev-env" (
+    :setup-env
+    @echo ------------------------------------------------
+    @echo downloading pip installer
+    @echo ------------------------------------------------
+    C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); open('get-pip.py', 'wb').write(r.read())"
+    for %%P in (%ALL_PYTHONS%) do (
+        @echo ------------------------------------------------
+        @echo installing pip for %%P
+        @echo ------------------------------------------------
+        %%P get-pip.py
+    )
+    for %%P in (%ALL_PYTHONS%) do (
+        @echo ------------------------------------------------
+        @echo installing deps for %%P
+        @echo ------------------------------------------------
+        rem mandatory / for unittests
+        %%P -m pip install unittest2 ipaddress mock wmi wheel pypiwin32 --upgrade
+        rem nice to have
+        %%P -m pip install ipdb pep8 pyflakes flake8 --upgrade
+    )
+    goto :eof
+)
+
+if "%1" == "flake8" (
+    :flake8
+    %PYTHON% -c "from flake8.main import main; main()"
     goto :eof
 )
 
 goto :help
 
 :error
-    echo last command exited with error code %errorlevel%
-    exit /b %errorlevel%
+    @echo ------------------------------------------------
+    @echo last command exited with error code %errorlevel%
+    @echo ------------------------------------------------
+    @exit /b %errorlevel%
     goto :eof
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil.egg-info/PKG-INFO
@@ -0,0 +1,434 @@
+Metadata-Version: 1.1
+Name: psutil
+Version: 3.1.1
+Summary: psutil is a cross-platform library for retrieving information onrunning processes and system utilization (CPU, memory, disks, network)in Python.
+Home-page: https://github.com/giampaolo/psutil
+Author: Giampaolo Rodola
+Author-email: g.rodola <at> gmail <dot> com
+License: BSD
+Description: .. image:: https://img.shields.io/pypi/dm/psutil.svg
+            :target: https://pypi.python.org/pypi/psutil#downloads
+            :alt: Downloads this month
+        
+        .. image:: https://api.travis-ci.org/giampaolo/psutil.png?branch=master
+            :target: https://travis-ci.org/giampaolo/psutil
+            :alt: Linux tests (Travis)
+        
+        .. image:: https://ci.appveyor.com/api/projects/status/qdwvw7v1t915ywr5/branch/master?svg=true
+            :target: https://ci.appveyor.com/project/giampaolo/psutil
+            :alt: Windows tests (Appveyor)
+        
+        .. image:: https://coveralls.io/repos/giampaolo/psutil/badge.svg?branch=master&service=github
+            :target: https://coveralls.io/github/giampaolo/psutil?branch=master
+            :alt: Test coverage (coverall.io)
+        
+        .. image:: https://img.shields.io/pypi/v/psutil.svg
+            :target: https://pypi.python.org/pypi/psutil/
+            :alt: Latest version
+        
+        .. image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
+            :target: https://github.com/giampaolo/psutil/
+            :alt: Github stars
+        
+        .. image:: https://img.shields.io/scrutinizer/g/giampaolo/psutil.svg
+            :target: https://scrutinizer-ci.com/g/giampaolo/psutil/
+            :alt: Code quality (scrutinizer-ci.com)
+        
+        .. image:: https://img.shields.io/pypi/l/psutil.svg
+            :target: https://pypi.python.org/pypi/psutil/
+            :alt: License
+        
+        ===========
+        Quick links
+        ===========
+        
+        - `Home page <https://github.com/giampaolo/psutil>`_
+        - `Documentation <http://pythonhosted.org/psutil/>`_
+        - `Installation <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+        - `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>`_
+        - `Development guide <https://github.com/giampaolo/psutil/blob/master/DEVGUIDE.rst>`_
+        - `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
+        
+        =======
+        Summary
+        =======
+        
+        psutil (python system and process utilities) is a cross-platform library for
+        retrieving information on **running processes** and **system utilization**
+        (CPU, memory, disks, network) in Python. It is useful mainly for **system
+        monitoring**, **profiling and limiting process resources** and **management of
+        running processes**. It implements many functionalities offered by command line
+        tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice,
+        ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports
+        **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, 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 <https://pypi.python.org/pypi?name=psutil&version=2.1.3&:action=files>`__ version).
+        `PyPy <http://pypy.org/>`__ is also known to work.
+        
+        ====================
+        Example applications
+        ====================
+        
+        .. image:: http://psutil.googlecode.com/svn/wiki/images/top-thumb.png
+            :target: http://psutil.googlecode.com/svn/wiki/images/top.png
+            :alt: top
+        
+        .. image:: http://psutil.googlecode.com/svn/wiki/images/nettop-thumb.png
+            :target: http://psutil.googlecode.com/svn/wiki/images/nettop.png
+            :alt: nettop
+        
+        .. image:: http://psutil.googlecode.com/svn/wiki/images/iotop-thumb.png
+            :target: http://psutil.googlecode.com/svn/wiki/images/iotop.png
+            :alt: iotop
+        
+        See also:
+        
+         * https://github.com/nicolargo/glances
+         * https://github.com/google/grr
+         * https://github.com/Jahaja/psdash
+        
+        ==============
+        Example usages
+        ==============
+        
+        CPU
+        ===
+        
+        .. code-block:: python
+        
+            >>> import psutil
+            >>> psutil.cpu_times()
+            scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0)
+            >>>
+            >>> for x in range(3):
+            ...     psutil.cpu_percent(interval=1)
+            ...
+            4.0
+            5.9
+            3.8
+            >>>
+            >>> for x in range(3):
+            ...     psutil.cpu_percent(interval=1, percpu=True)
+            ...
+            [4.0, 6.9, 3.7, 9.2]
+            [7.0, 8.5, 2.4, 2.1]
+            [1.2, 9.0, 9.9, 7.2]
+            >>>
+            >>>
+            >>> for x in range(3):
+            ...     psutil.cpu_times_percent(interval=1, percpu=False)
+            ...
+            scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+            scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+            scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+            >>>
+            >>> psutil.cpu_count()
+            4
+            >>> psutil.cpu_count(logical=False)
+            2
+            >>>
+        
+        Memory
+        ======
+        
+        .. code-block:: python
+        
+            >>> psutil.virtual_memory()
+            svmem(total=8374149120, available=2081050624, percent=75.1, used=8074080256, free=300068864, active=3294920704, inactive=1361616896, buffers=529895424, cached=1251086336)
+            >>> psutil.swap_memory()
+            sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
+            >>>
+        
+        Disks
+        =====
+        
+        .. code-block:: python
+        
+            >>> psutil.disk_partitions()
+            [sdiskpart(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
+             sdiskpart(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
+            >>>
+            >>> psutil.disk_usage('/')
+            sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
+            >>>
+            >>> psutil.disk_io_counters(perdisk=False)
+            sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568)
+            >>>
+        
+        Network
+        =======
+        
+        .. code-block:: python
+        
+            >>> psutil.net_io_counters(pernic=True)
+            {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
+             'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
+            >>>
+            >>> psutil.net_connections()
+            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED', pid=1254),
+             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING', pid=2987),
+             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED', pid=None),
+             pconn(fd=-1, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT', pid=None)
+             ...]
+            >>>
+            >>> psutil.net_if_addrs()
+            {'lo': [snic(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1'),
+                    snic(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None),
+                    snic(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00')],
+             'wlan0': [snic(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255'),
+                       snic(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None),
+                       snic(family=<AddressFamily.AF_LINK: 17>, address='c4:85:08:45:06:41', netmask=None, broadcast='ff:ff:ff:ff:ff:ff')]}
+            >>>
+            >>> psutil.net_if_stats()
+            {'eth0': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_FULL: 2>, speed=100, mtu=1500),
+             'lo': snicstats(isup=True, duplex=<NicDuplex.NIC_DUPLEX_UNKNOWN: 0>, speed=0, mtu=65536)}
+        
+        Other system info
+        =================
+        
+        .. code-block:: python
+        
+            >>> psutil.users()
+            [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
+             user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+            >>>
+            >>> psutil.boot_time()
+            1365519115.0
+            >>>
+        
+        Process management
+        ==================
+        
+        .. code-block:: python
+        
+            >>> import psutil
+            >>> psutil.pids()
+            [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224,
+             268, 1215, 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355,
+             2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245,
+             4263, 4282, 4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358,
+             4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446, 5167, 5234, 5235,
+             5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071]
+            >>>
+            >>> p = psutil.Process(7055)
+            >>> p.name()
+            'python'
+            >>> p.exe()
+            '/usr/bin/python'
+            >>> p.cwd()
+            '/home/giampaolo'
+            >>> p.cmdline()
+            ['/usr/bin/python', 'main.py']
+            >>>
+            >>> p.status()
+            'running'
+            >>> p.username()
+            'giampaolo'
+            >>> p.create_time()
+            1267551141.5019531
+            >>> p.terminal()
+            '/dev/pts/0'
+            >>>
+            >>> p.uids()
+            puids(real=1000, effective=1000, saved=1000)
+            >>> p.gids()
+            pgids(real=1000, effective=1000, saved=1000)
+            >>>
+            >>> p.cpu_times()
+            pcputimes(user=1.02, system=0.31)
+            >>> p.cpu_percent(interval=1.0)
+            12.1
+            >>> p.cpu_affinity()
+            [0, 1, 2, 3]
+            >>> p.cpu_affinity([0])  # set
+            >>>
+            >>> p.memory_percent()
+            0.63423
+            >>>
+            >>> p.memory_info()
+            pmem(rss=7471104, vms=68513792)
+            >>> p.memory_info_ex()
+            extmem(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
+            >>> p.memory_maps()
+            [pmmap_grouped(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
+             pmmap_grouped(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
+             pmmap_grouped(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
+             pmmap_grouped(path='[heap]', rss=54653, anonymous=8192, swap=0),
+             pmmap_grouped(path='[stack]', rss=1542, anonymous=166, swap=0),
+             ...]
+            >>>
+            >>> p.io_counters()
+            pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
+            >>>
+            >>> p.open_files()
+            [popenfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
+            >>>
+            >>> p.connections()
+            [pconn(fd=115, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 48776), raddr=('93.186.135.91', 80), status='ESTABLISHED'),
+             pconn(fd=117, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 43761), raddr=('72.14.234.100', 80), status='CLOSING'),
+             pconn(fd=119, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 60759), raddr=('72.14.234.104', 80), status='ESTABLISHED'),
+             pconn(fd=123, family=<AddressFamily.AF_INET: 2>, type=<SocketType.SOCK_STREAM: 1>, laddr=('10.0.0.1', 51314), raddr=('72.14.234.83', 443), status='SYN_SENT')]
+            >>>
+            >>> p.num_threads()
+            4
+            >>> p.num_fds()
+            8
+            >>> p.threads()
+            [pthread(id=5234, user_time=22.5, system_time=9.2891),
+             pthread(id=5235, user_time=0.0, system_time=0.0),
+             pthread(id=5236, user_time=0.0, system_time=0.0),
+             pthread(id=5237, user_time=0.0707, system_time=1.1)]
+            >>>
+            >>> p.num_ctx_switches()
+            pctxsw(voluntary=78, involuntary=19)
+            >>>
+            >>> p.nice()
+            0
+            >>> p.nice(10)  # set
+            >>>
+            >>> p.ionice(psutil.IOPRIO_CLASS_IDLE)  # IO priority (Win and Linux only)
+            >>> p.ionice()
+            pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
+            >>>
+            >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5))  # set resource limits (Linux only)
+            >>> p.rlimit(psutil.RLIMIT_NOFILE)
+            (5, 5)
+            >>>
+            >>> p.suspend()
+            >>> p.resume()
+            >>>
+            >>> p.terminate()
+            >>> p.wait(timeout=3)
+            0
+            >>>
+            >>> psutil.test()
+            USER         PID %CPU %MEM     VSZ     RSS TTY        START    TIME  COMMAND
+            root           1  0.0  0.0   24584    2240            Jun17   00:00  init
+            root           2  0.0  0.0       0       0            Jun17   00:00  kthreadd
+            root           3  0.0  0.0       0       0            Jun17   00:05  ksoftirqd/0
+            ...
+            giampaolo  31475  0.0  0.0   20760    3024 /dev/pts/0 Jun19   00:00  python2.4
+            giampaolo  31721  0.0  2.2  773060  181896            00:04   10:30  chrome
+            root       31763  0.0  0.0       0       0            00:05   00:00  kworker/0:1
+            >>>
+        
+        Further process APIs
+        ====================
+        
+        .. code-block:: python
+        
+            >>> for p in psutil.process_iter():
+            ...     print(p)
+            ...
+            psutil.Process(pid=1, name='init')
+            psutil.Process(pid=2, name='kthreadd')
+            psutil.Process(pid=3, name='ksoftirqd/0')
+            ...
+            >>>
+            >>> def on_terminate(proc):
+            ...     print("process {} terminated".format(proc))
+            ...
+            >>> # waits for multiple processes to terminate
+            >>> gone, alive = psutil.wait_procs(procs_list, 3, callback=on_terminate)
+            >>>
+        
+        ======
+        Donate
+        ======
+        
+        A lot of time and effort went into making psutil as it is right now.
+        If you feel psutil is useful to you or your business and want to support its future development please consider donating me (`Giampaolo Rodola' <http://grodola.blogspot.com/p/about.html>`_) some money.
+        I only ask for a small donation, but of course I appreciate any amount.
+        
+        .. image:: http://www.paypal.com/en_US/i/btn/x-click-but04.gif
+            :target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A9ZS7PKKRM3S8
+            :alt: Donate via PayPal
+        
+        Don't want to donate money? Then maybe you could `write me a recommendation on Linkedin <http://www.linkedin.com/in/grodola>`_.
+        
+        ============
+        Mailing list
+        ============
+        
+        http://groups.google.com/group/psutil/
+        
+        ========
+        Timeline
+        ========
+        
+        - 2015-07-15: `psutil-3.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.1.tar.gz>`_
+        - 2015-07-15: `psutil-3.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.1.0.tar.gz>`_
+        - 2015-06-18: `psutil-3.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.1.tar.gz>`_
+        - 2015-06-13: `psutil-3.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-3.0.0.tar.gz>`_
+        - 2015-02-02: `psutil-2.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.1.tar.gz>`_
+        - 2015-01-06: `psutil-2.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.2.0.tar.gz>`_
+        - 2014-09-26: `psutil-2.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.3.tar.gz>`_
+        - 2014-09-21: `psutil-2.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.2.tar.gz>`_
+        - 2014-04-30: `psutil-2.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz>`_
+        - 2014-04-08: `psutil-2.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.1.0.tar.gz>`_
+        - 2014-03-10: `psutil-2.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-2.0.0.tar.gz>`_
+        - 2013-11-25: `psutil-1.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.1.tar.gz>`_
+        - 2013-11-20: `psutil-1.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.2.0.tar.gz>`_
+        - 2013-11-07: `psutil-1.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.3.tar.gz>`_
+        - 2013-10-22: `psutil-1.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.2.tar.gz>`_
+        - 2013-10-08: `psutil-1.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.1.tar.gz>`_
+        - 2013-09-28: `psutil-1.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.1.0.tar.gz>`_
+        - 2013-07-12: `psutil-1.0.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.1.tar.gz>`_
+        - 2013-07-10: `psutil-1.0.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-1.0.0.tar.gz>`_
+        - 2013-05-03: `psutil-0.7.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.1.tar.gz>`_
+        - 2013-04-12: `psutil-0.7.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.7.0.tar.gz>`_
+        - 2012-08-16: `psutil-0.6.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.1.tar.gz>`_
+        - 2012-08-13: `psutil-0.6.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.6.0.tar.gz>`_
+        - 2012-06-29: `psutil-0.5.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.1.tar.gz>`_
+        - 2012-06-27: `psutil-0.5.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.5.0.tar.gz>`_
+        - 2011-12-14: `psutil-0.4.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.1.tar.gz>`_
+        - 2011-10-29: `psutil-0.4.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.4.0.tar.gz>`_
+        - 2011-07-08: `psutil-0.3.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.3.0.tar.gz>`_
+        - 2011-03-20: `psutil-0.2.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.1.tar.gz>`_
+        - 2010-11-13: `psutil-0.2.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.2.0.tar.gz>`_
+        - 2010-03-02: `psutil-0.1.3.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.3.tar.gz>`_
+        - 2009-05-06: `psutil-0.1.2.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.2.tar.gz>`_
+        - 2009-03-06: `psutil-0.1.1.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.1.tar.gz>`_
+        - 2009-01-27: `psutil-0.1.0.tar.gz <https://pypi.python.org/packages/source/p/psutil/psutil-0.1.0.tar.gz>`_
+        
+Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,pstree,monitoring,ulimit,prlimit
+Platform: Platform Independent
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: Win32 (MS Windows)
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000
+Classifier: Operating System :: Microsoft
+Classifier: Operating System :: OS Independent
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: SunOS/Solaris
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.0
+Classifier: Programming Language :: Python :: 3.1
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: System :: Benchmark
+Classifier: Topic :: System :: Hardware
+Classifier: Topic :: System :: Monitoring
+Classifier: Topic :: System :: Networking :: Monitoring
+Classifier: Topic :: System :: Networking
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil.egg-info/SOURCES.txt
@@ -0,0 +1,96 @@
+.coveragerc
+.git-pre-commit
+.gitignore
+.travis.yml
+CREDITS
+HISTORY.rst
+INSTALL.rst
+LICENSE
+MANIFEST.in
+Makefile
+README.rst
+TODO
+make.bat
+setup.py
+tox.ini
+docs/Makefile
+docs/README
+docs/conf.py
+docs/index.rst
+docs/make.bat
+docs/xxx
+docs/_static/copybutton.js
+docs/_static/favicon.ico
+docs/_static/logo.png
+docs/_static/sidebar.js
+docs/_template/globaltoc.html
+docs/_template/indexcontent.html
+docs/_template/indexsidebar.html
+docs/_template/page.html
+docs/_themes/pydoctheme/theme.conf
+docs/_themes/pydoctheme/static/pydoctheme.css
+examples/disk_usage.py
+examples/free.py
+examples/ifconfig.py
+examples/iotop.py
+examples/killall.py
+examples/meminfo.py
+examples/netstat.py
+examples/nettop.py
+examples/pidof.py
+examples/pmap.py
+examples/process_detail.py
+examples/ps.py
+examples/pstree.py
+examples/top.py
+examples/who.py
+psutil/__init__.py
+psutil/_common.py
+psutil/_compat.py
+psutil/_psbsd.py
+psutil/_pslinux.py
+psutil/_psosx.py
+psutil/_psposix.py
+psutil/_pssunos.py
+psutil/_psutil_bsd.c
+psutil/_psutil_bsd.h
+psutil/_psutil_common.c
+psutil/_psutil_common.h
+psutil/_psutil_linux.c
+psutil/_psutil_linux.h
+psutil/_psutil_osx.c
+psutil/_psutil_osx.h
+psutil/_psutil_posix.c
+psutil/_psutil_posix.h
+psutil/_psutil_sunos.c
+psutil/_psutil_sunos.h
+psutil/_psutil_windows.c
+psutil/_psutil_windows.h
+psutil/_pswindows.py
+psutil.egg-info/PKG-INFO
+psutil.egg-info/SOURCES.txt
+psutil.egg-info/dependency_links.txt
+psutil.egg-info/top_level.txt
+psutil/arch/bsd/process_info.c
+psutil/arch/bsd/process_info.h
+psutil/arch/osx/process_info.c
+psutil/arch/osx/process_info.h
+psutil/arch/windows/glpi.h
+psutil/arch/windows/inet_ntop.c
+psutil/arch/windows/inet_ntop.h
+psutil/arch/windows/ntextapi.h
+psutil/arch/windows/process_handles.c
+psutil/arch/windows/process_handles.h
+psutil/arch/windows/process_info.c
+psutil/arch/windows/process_info.h
+psutil/arch/windows/security.c
+psutil/arch/windows/security.h
+test/README.rst
+test/_bsd.py
+test/_linux.py
+test/_osx.py
+test/_posix.py
+test/_sunos.py
+test/_windows.py
+test/test_memory_leaks.py
+test/test_psutil.py
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil.egg-info/dependency_links.txt
@@ -0,0 +1,1 @@
+
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil.egg-info/top_level.txt
@@ -0,0 +1,1 @@
+psutil
--- a/python/psutil/psutil/__init__.py
+++ b/python/psutil/psutil/__init__.py
@@ -7,116 +7,88 @@
 
 """psutil is a cross-platform library for retrieving information on
 running processes and system utilization (CPU, memory, disks, network)
 in Python.
 """
 
 from __future__ import division
 
-__author__ = "Giampaolo Rodola'"
-__version__ = "2.1.3"
-version_info = tuple([int(num) for num in __version__.split('.')])
-
-__all__ = [
-    # exceptions
-    "Error", "NoSuchProcess", "AccessDenied", "TimeoutExpired",
-    # constants
-    "version_info", "__version__",
-    "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
-    "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
-    "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED",
-    "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
-    "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
-    "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE",
-    # classes
-    "Process", "Popen",
-    # functions
-    "pid_exists", "pids", "process_iter", "wait_procs",             # proc
-    "virtual_memory", "swap_memory",                                # memory
-    "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count",   # cpu
-    "net_io_counters", "net_connections",                           # network
-    "disk_io_counters", "disk_partitions", "disk_usage",            # disk
-    "users", "boot_time",                                           # others
-]
-
+import collections
+import errno
+import functools
+import os
+import signal
+import subprocess
 import sys
-import os
 import time
-import signal
-import warnings
-import errno
-import subprocess
 try:
     import pwd
 except ImportError:
     pwd = None
 
-from psutil._common import memoize
-from psutil._compat import property, callable, long, defaultdict
-from psutil._compat import (wraps as _wraps,
-                            PY3 as _PY3)
-from psutil._common import (deprecated_method as _deprecated_method,
-                            deprecated as _deprecated,
-                            sdiskio as _nt_sys_diskio,
-                            snetio as _nt_sys_netio)
+from . import _common
+from ._common import memoize
+from ._compat import callable, long
+from ._compat import PY3 as _PY3
+
+from ._common import (STATUS_RUNNING,  # NOQA
+                      STATUS_SLEEPING,
+                      STATUS_DISK_SLEEP,
+                      STATUS_STOPPED,
+                      STATUS_TRACING_STOP,
+                      STATUS_ZOMBIE,
+                      STATUS_DEAD,
+                      STATUS_WAKING,
+                      STATUS_LOCKED,
+                      STATUS_IDLE,  # bsd
+                      STATUS_WAITING)  # bsd
 
-from psutil._common import (STATUS_RUNNING,  # NOQA
-                            STATUS_SLEEPING,
-                            STATUS_DISK_SLEEP,
-                            STATUS_STOPPED,
-                            STATUS_TRACING_STOP,
-                            STATUS_ZOMBIE,
-                            STATUS_DEAD,
-                            STATUS_WAKING,
-                            STATUS_LOCKED,
-                            STATUS_IDLE,  # bsd
-                            STATUS_WAITING,  # bsd
-                            STATUS_LOCKED)  # bsd
+from ._common import (CONN_ESTABLISHED,
+                      CONN_SYN_SENT,
+                      CONN_SYN_RECV,
+                      CONN_FIN_WAIT1,
+                      CONN_FIN_WAIT2,
+                      CONN_TIME_WAIT,
+                      CONN_CLOSE,
+                      CONN_CLOSE_WAIT,
+                      CONN_LAST_ACK,
+                      CONN_LISTEN,
+                      CONN_CLOSING,
+                      CONN_NONE)
 
-from psutil._common import (CONN_ESTABLISHED,
-                            CONN_SYN_SENT,
-                            CONN_SYN_RECV,
-                            CONN_FIN_WAIT1,
-                            CONN_FIN_WAIT2,
-                            CONN_TIME_WAIT,
-                            CONN_CLOSE,
-                            CONN_CLOSE_WAIT,
-                            CONN_LAST_ACK,
-                            CONN_LISTEN,
-                            CONN_CLOSING,
-                            CONN_NONE)
+from ._common import (NIC_DUPLEX_FULL,  # NOQA
+                      NIC_DUPLEX_HALF,
+                      NIC_DUPLEX_UNKNOWN)
 
 if sys.platform.startswith("linux"):
-    import psutil._pslinux as _psplatform
-    from psutil._pslinux import (phymem_buffers,  # NOQA
-                                 cached_phymem)
+    from . import _pslinux as _psplatform
 
-    from psutil._pslinux import (IOPRIO_CLASS_NONE,  # NOQA
-                                 IOPRIO_CLASS_RT,
-                                 IOPRIO_CLASS_BE,
-                                 IOPRIO_CLASS_IDLE)
+    from ._pslinux import (IOPRIO_CLASS_NONE,  # NOQA
+                           IOPRIO_CLASS_RT,
+                           IOPRIO_CLASS_BE,
+                           IOPRIO_CLASS_IDLE)
     # Linux >= 2.6.36
     if _psplatform.HAS_PRLIMIT:
-        from _psutil_linux import (RLIM_INFINITY,  # NOQA
-                                   RLIMIT_AS,
-                                   RLIMIT_CORE,
-                                   RLIMIT_CPU,
-                                   RLIMIT_DATA,
-                                   RLIMIT_FSIZE,
-                                   RLIMIT_LOCKS,
-                                   RLIMIT_MEMLOCK,
-                                   RLIMIT_NOFILE,
-                                   RLIMIT_NPROC,
-                                   RLIMIT_RSS,
-                                   RLIMIT_STACK)
+        from ._psutil_linux import (RLIM_INFINITY,  # NOQA
+                                    RLIMIT_AS,
+                                    RLIMIT_CORE,
+                                    RLIMIT_CPU,
+                                    RLIMIT_DATA,
+                                    RLIMIT_FSIZE,
+                                    RLIMIT_LOCKS,
+                                    RLIMIT_MEMLOCK,
+                                    RLIMIT_NOFILE,
+                                    RLIMIT_NPROC,
+                                    RLIMIT_RSS,
+                                    RLIMIT_STACK)
         # Kinda ugly but considerably faster than using hasattr() and
         # setattr() against the module object (we are at import time:
         # speed matters).
-        import _psutil_linux
+        from . import _psutil_linux
         try:
             RLIMIT_MSGQUEUE = _psutil_linux.RLIMIT_MSGQUEUE
         except AttributeError:
             pass
         try:
             RLIMIT_NICE = _psutil_linux.RLIMIT_NICE
         except AttributeError:
             pass
@@ -130,133 +102,203 @@ if sys.platform.startswith("linux"):
             pass
         try:
             RLIMIT_SIGPENDING = _psutil_linux.RLIMIT_SIGPENDING
         except AttributeError:
             pass
         del _psutil_linux
 
 elif sys.platform.startswith("win32"):
-    import psutil._pswindows as _psplatform
-    from _psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,  # NOQA
-                                 BELOW_NORMAL_PRIORITY_CLASS,
-                                 HIGH_PRIORITY_CLASS,
-                                 IDLE_PRIORITY_CLASS,
-                                 NORMAL_PRIORITY_CLASS,
-                                 REALTIME_PRIORITY_CLASS)
-    from psutil._pswindows import CONN_DELETE_TCB  # NOQA
+    from . import _pswindows as _psplatform
+    from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,  # NOQA
+                                  BELOW_NORMAL_PRIORITY_CLASS,
+                                  HIGH_PRIORITY_CLASS,
+                                  IDLE_PRIORITY_CLASS,
+                                  NORMAL_PRIORITY_CLASS,
+                                  REALTIME_PRIORITY_CLASS)
+    from ._pswindows import CONN_DELETE_TCB  # NOQA
 
 elif sys.platform.startswith("darwin"):
-    import psutil._psosx as _psplatform
+    from . import _psosx as _psplatform
 
 elif sys.platform.startswith("freebsd"):
-    import psutil._psbsd as _psplatform
+    from . import _psbsd as _psplatform
 
 elif sys.platform.startswith("sunos"):
-    import psutil._pssunos as _psplatform
-    from psutil._pssunos import (CONN_IDLE,  # NOQA
-                                 CONN_BOUND)
+    from . import _pssunos as _psplatform
+    from ._pssunos import (CONN_IDLE,  # NOQA
+                           CONN_BOUND)
 
-else:
+else:  # pragma: no cover
     raise NotImplementedError('platform %s is not supported' % sys.platform)
 
+
+__all__ = [
+    # exceptions
+    "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
+    "TimeoutExpired",
+    # constants
+    "version_info", "__version__",
+    "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
+    "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
+    "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED",
+    "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
+    "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
+    "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", "CONN_NONE",
+    "AF_LINK",
+    "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN",
+    # classes
+    "Process", "Popen",
+    # functions
+    "pid_exists", "pids", "process_iter", "wait_procs",             # proc
+    "virtual_memory", "swap_memory",                                # memory
+    "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count",   # cpu
+    "net_io_counters", "net_connections", "net_if_addrs",           # network
+    "net_if_stats",
+    "disk_io_counters", "disk_partitions", "disk_usage",            # disk
+    "users", "boot_time",                                           # others
+]
 __all__.extend(_psplatform.__extra__all__)
-
-
+__author__ = "Giampaolo Rodola'"
+__version__ = "3.1.1"
+version_info = tuple([int(num) for num in __version__.split('.')])
+AF_LINK = _psplatform.AF_LINK
 _TOTAL_PHYMEM = None
 _POSIX = os.name == 'posix'
 _WINDOWS = os.name == 'nt'
 _timer = getattr(time, 'monotonic', time.time)
 
 
+# Sanity check in case the user messed up with psutil installation
+# or did something weird with sys.path. In this case we might end
+# up importing a python module using a C extension module which
+# was compiled for a different version of psutil.
+# We want to prevent that by failing sooner rather than later.
+# See: https://github.com/giampaolo/psutil/issues/564
+if (int(__version__.replace('.', '')) !=
+        getattr(_psplatform.cext, 'version', None)):
+    msg = "version conflict: %r C extension module was built for another " \
+          "version of psutil (different than %s)" % (_psplatform.cext.__file__,
+                                                     __version__)
+    raise ImportError(msg)
+
+
 # =====================================================================
 # --- exceptions
 # =====================================================================
 
 class Error(Exception):
     """Base exception class. All other psutil exceptions inherit
     from this one.
     """
 
+    def __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 (zombie).
+    or no longer exists.
     """
 
     def __init__(self, pid, name=None, msg=None):
-        Error.__init__(self)
+        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
 
-    def __str__(self):
-        return self.msg
+
+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):
+        Error.__init__(self, msg)
+        self.pid = pid
+        self.ppid = ppid
+        self.name = name
+        self.msg = msg
+        if msg is None:
+            if name and ppid:
+                details = "(pid=%s, name=%s, ppid=%s)" % (
+                    self.pid, repr(self.name), self.ppid)
+            elif name:
+                details = "(pid=%s, name=%s)" % (self.pid, repr(self.name))
+            else:
+                details = "(pid=%s)" % self.pid
+            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)
+        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 = ""
 
-    def __str__(self):
-        return 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)
+        Error.__init__(self, "timeout after %s seconds" % seconds)
         self.seconds = seconds
         self.pid = pid
         self.name = name
-        self.msg = "timeout after %s seconds" % seconds
         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
 
-    def __str__(self):
-        return self.msg
 
 # push exception classes into platform specific module namespace
 _psplatform.NoSuchProcess = NoSuchProcess
+_psplatform.ZombieProcess = ZombieProcess
 _psplatform.AccessDenied = AccessDenied
 _psplatform.TimeoutExpired = TimeoutExpired
 
 
 # =====================================================================
 # --- Process class
 # =====================================================================
 
+
 def _assert_pid_not_reused(fun):
     """Decorator which raises NoSuchProcess in case a process is no
     longer running or its PID has been reused.
     """
-    @_wraps(fun)
+    @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         if not self.is_running():
             raise NoSuchProcess(self.pid, self._name)
         return fun(self, *args, **kwargs)
     return wrapper
 
 
 class Process(object):
@@ -320,32 +362,39 @@ class Process(object):
         # cache creation time for later use in is_running() method
         try:
             self.create_time()
         except AccessDenied:
             # we should never get here as AFAIK we're able to get
             # process creation time on all platforms even as a
             # limited user
             pass
+        except ZombieProcess:
+            # Let's consider a zombie process as legitimate as
+            # tehcnically it's still alive (it can be queried,
+            # although not always, and it's returned by pids()).
+            pass
         except NoSuchProcess:
             if not _ignore_nsp:
                 msg = 'no process found with pid %s' % pid
                 raise NoSuchProcess(pid, None, msg)
             else:
                 self._gone = True
         # This pair is supposed to indentify a Process instance
         # univocally over time (the PID alone is not enough as
         # it might refer to a process whose PID has been reused).
         # This will be used later in __eq__() and is_running().
         self._ident = (self.pid, self._create_time)
 
     def __str__(self):
         try:
             pid = self.pid
             name = repr(self.name())
+        except ZombieProcess:
+            details = "(pid=%s (zombie))" % self.pid
         except NoSuchProcess:
             details = "(pid=%s (terminated))" % self.pid
         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)
@@ -365,61 +414,46 @@ class Process(object):
 
     def __hash__(self):
         if self._hash is None:
             self._hash = hash(self._ident)
         return self._hash
 
     # --- utility methods
 
-    def as_dict(self, attrs=[], ad_value=None):
+    def as_dict(self, attrs=None, ad_value=None):
         """Utility method returning process information as a
         hashable dictionary.
 
         If 'attrs' is specified it must be a list of strings
         reflecting available Process class' attribute names
         (e.g. ['cpu_times', 'name']) else all public (read
         only) attributes are assumed.
 
         'ad_value' is the value which gets assigned in case
-        AccessDenied  exception is raised when retrieving that
-        particular process information.
+        AccessDenied or ZombieProcess exception is raised when
+        retrieving that particular process information.
         """
         excluded_names = set(
             ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
              'is_running', 'as_dict', 'parent', 'children', 'rlimit'])
         retdict = dict()
-        ls = set(attrs or [x for x in dir(self) if not x.startswith('get')])
+        ls = set(attrs or [x for x in dir(self)])
         for name in ls:
             if name.startswith('_'):
                 continue
-            if name.startswith('set_'):
-                continue
-            if name.startswith('get_'):
-                msg = "%s() is deprecated; use %s() instead" % (name, name[4:])
-                warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-                name = name[4:]
-                if name in ls:
-                    continue
-            if name == 'getcwd':
-                msg = "getcwd() is deprecated; use cwd() instead"
-                warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-                name = 'cwd'
-                if name in ls:
-                    continue
-
             if name in excluded_names:
                 continue
             try:
                 attr = getattr(self, name)
                 if callable(attr):
                     ret = attr()
                 else:
                     ret = attr
-            except AccessDenied:
+            except (AccessDenied, ZombieProcess):
                 ret = ad_value
             except NotImplementedError:
                 # in case of not implemented functionality (may happen
                 # on old or exotic systems) we want to crash only if
                 # the user explicitly asked for that particular attr
                 if attrs:
                     raise
                 continue
@@ -428,19 +462,20 @@ class Process(object):
 
     def parent(self):
         """Return the parent process as a Process object pre-emptively
         checking whether PID has been reused.
         If no parent is known return None.
         """
         ppid = self.ppid()
         if ppid is not None:
+            ctime = self.create_time()
             try:
                 parent = Process(ppid)
-                if parent.create_time() <= self.create_time():
+                if parent.create_time() <= ctime:
                     return parent
                 # ...else ppid has been reused by another process
             except NoSuchProcess:
                 pass
 
     def is_running(self):
         """Return whether this process is running.
         It also checks if PID has been reused by another process in
@@ -475,18 +510,17 @@ class Process(object):
         # https://github.com/giampaolo/psutil/issues/321
         # http://stackoverflow.com/questions/356722/
 
         # XXX should we check creation time here rather than in
         # Process.parent()?
         if _POSIX:
             return self._proc.ppid()
         else:
-            if self._ppid is None:
-                self._ppid = self._proc.ppid()
+            self._ppid = self._ppid or self._proc.ppid()
             return self._ppid
 
     def name(self):
         """The process name. The return value is cached after first call."""
         if self._name is None:
             name = self._proc.name()
             if _POSIX and len(name) >= 15:
                 # On UNIX the name gets truncated to the first 15 characters.
@@ -515,29 +549,28 @@ class Process(object):
             # try to guess exe from cmdline[0] in absence of a native
             # exe representation
             cmdline = self.cmdline()
             if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'):
                 exe = cmdline[0]  # the possible exe
                 # Attempt to guess only in case of an absolute path.
                 # It is not safe otherwise as the process might have
                 # changed cwd.
-                if (os.path.isabs(exe)
-                        and os.path.isfile(exe)
-                        and os.access(exe, os.X_OK)):
+                if (os.path.isabs(exe) and
+                        os.path.isfile(exe) and
+                        os.access(exe, os.X_OK)):
                     return exe
             if isinstance(fallback, AccessDenied):
                 raise fallback
             return fallback
 
         if self._exe is None:
             try:
                 exe = self._proc.exe()
-            except AccessDenied:
-                err = sys.exc_info()[1]
+            except AccessDenied as err:
                 return guess_it(fallback=err)
             else:
                 if not exe:
                     # underlying implementation can legitimately return an
                     # empty string; if that's the case we don't want to
                     # raise AD while guessing from the cmdline
                     try:
                         exe = guess_it(fallback=exe)
@@ -547,28 +580,36 @@ class Process(object):
         return self._exe
 
     def cmdline(self):
         """The command line this process has been called with."""
         return self._proc.cmdline()
 
     def status(self):
         """The process current status as a STATUS_* constant."""
-        return self._proc.status()
+        try:
+            return self._proc.status()
+        except ZombieProcess:
+            return STATUS_ZOMBIE
 
     def username(self):
         """The name of the user that owns the process.
         On UNIX this is calculated by using *real* process uid.
         """
         if _POSIX:
             if pwd is None:
                 # might happen if python was installed from sources
                 raise ImportError(
                     "requires pwd module shipped with standard python")
-            return pwd.getpwuid(self.uids().real).pw_name
+            real_uid = self.uids().real
+            try:
+                return pwd.getpwuid(real_uid).pw_name
+            except KeyError:
+                # the uid can't be resolved by the system
+                return str(real_uid)
         else:
             return self._proc.username()
 
     def create_time(self):
         """The process creation time as a floating point number
         expressed in seconds since the epoch, in UTC.
         The return value is cached after first call.
         """
@@ -639,17 +680,17 @@ class Process(object):
 
             On Windows only 'ioclass' is used and it can be set to 2
             (normal), 1 (low) or 0 (very low).
 
             Available on Linux and Windows > Vista only.
             """
             if ioclass is None:
                 if value is not None:
-                    raise ValueError("'ioclass' must be specified")
+                    raise ValueError("'ioclass' argument must be specified")
                 return self._proc.ionice_get()
             else:
                 return self._proc.ionice_set(ioclass, value)
 
     # Linux only
     if hasattr(_psplatform.Process, "rlimit"):
 
         def rlimit(self, resource, limits=None):
@@ -662,28 +703,32 @@ class Process(object):
             See "man prlimit" for further info.
             Available on Linux only.
             """
             if limits is None:
                 return self._proc.rlimit(resource)
             else:
                 return self._proc.rlimit(resource, limits)
 
-    # Windows and Linux only
+    # Windows, Linux and BSD only
     if hasattr(_psplatform.Process, "cpu_affinity_get"):
 
         def cpu_affinity(self, cpus=None):
             """Get or set process CPU affinity.
             If specified 'cpus' must be a list of CPUs for which you
             want to set the affinity (e.g. [0, 1]).
+            (Windows, Linux and BSD only).
             """
+            # Automatically remove duplicates both on get and
+            # set (for get it's not really necessary, it's
+            # just for extra safety).
             if cpus is None:
-                return self._proc.cpu_affinity_get()
+                return list(set(self._proc.cpu_affinity_get()))
             else:
-                self._proc.cpu_affinity_set(cpus)
+                self._proc.cpu_affinity_set(list(set(cpus)))
 
     if _WINDOWS:
 
         def num_handles(self):
             """Return the number of handles opened by this process
             (Windows only).
             """
             return self._proc.num_handles()
@@ -745,59 +790,59 @@ class Process(object):
                 # 'slow' version, common to all platforms except Windows
                 for p in process_iter():
                     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)
-                    except NoSuchProcess:
+                    except (NoSuchProcess, ZombieProcess):
                         pass
             else:
                 # 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:
+                        except (NoSuchProcess, ZombieProcess):
                             pass
         else:
             # construct a dict where 'values' are all the processes
             # having 'key' as their parent
-            table = defaultdict(list)
+            table = collections.defaultdict(list)
             if ppid_map is None:
                 for p in process_iter():
                     try:
                         table[p.ppid()].append(p)
-                    except NoSuchProcess:
+                    except (NoSuchProcess, ZombieProcess):
                         pass
             else:
                 for pid, ppid in ppid_map.items():
                     try:
                         p = Process(pid)
                         table[ppid].append(p)
-                    except NoSuchProcess:
+                    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]:
                     try:
                         # 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:
+                    except (NoSuchProcess, ZombieProcess):
                         pass
                     else:
                         if intime:
                             ret.append(child)
                             if child.pid not in checkpids:
                                 checkpids.append(child.pid)
         return ret
 
@@ -826,19 +871,21 @@ class Process(object):
           >>> # non-blocking (percentage since last call)
           >>> p.cpu_percent(interval=None)
           2.9
           >>>
         """
         blocking = interval is not None and interval > 0.0
         num_cpus = cpu_count()
         if _POSIX:
-            timer = lambda: _timer() * num_cpus
+            def timer():
+                return _timer() * num_cpus
         else:
-            timer = lambda: sum(cpu_times())
+            def timer():
+                return sum(cpu_times())
         if blocking:
             st1 = timer()
             pt1 = self._proc.cpu_times()
             time.sleep(interval)
             st2 = timer()
             pt2 = self._proc.cpu_times()
         else:
             st1 = self._last_sys_cpu_times
@@ -903,17 +950,17 @@ class Process(object):
         # use cached value if available
         total_phymem = _TOTAL_PHYMEM or virtual_memory().total
         try:
             return (rss / float(total_phymem)) * 100
         except ZeroDivisionError:
             return 0.0
 
     def memory_maps(self, grouped=True):
-        """Return process' mapped memory regions as a list of nameduples
+        """Return process' mapped memory regions as a list of namedtuples
         whose fields are variable depending on the platform.
 
         If 'grouped' is True the mapped regions with the same 'path'
         are grouped together and the different memory fields are summed.
 
         If 'grouped' is False every mapped region is shown as a single
         entity and the namedtuple will also include the mapped region's
         address space ('addr') and permission set ('perms').
@@ -959,20 +1006,25 @@ class Process(object):
         udp6            UDP over IPv6
         unix            UNIX socket (both UDP and TCP protocols)
         all             the sum of all the possible families and protocols
         """
         return self._proc.connections(kind)
 
     if _POSIX:
         def _send_signal(self, sig):
+            if self.pid == 0:
+                # see "man 2 kill"
+                raise ValueError(
+                    "preventing sending signal to process with PID 0 as it "
+                    "would affect every process in the process group of the "
+                    "calling process (os.getpid()) instead of PID 0")
             try:
                 os.kill(self.pid, sig)
-            except OSError:
-                err = sys.exc_info()[1]
+            except OSError as err:
                 if err.errno == errno.ESRCH:
                     self._gone = True
                     raise NoSuchProcess(self.pid, self._name)
                 if err.errno == errno.EPERM:
                     raise AccessDenied(self.pid, self._name)
                 raise
 
     @_assert_pid_not_reused
@@ -1044,129 +1096,22 @@ class Process(object):
         raise TimeoutExpired.
 
         To wait for multiple Process(es) use psutil.wait_procs().
         """
         if timeout is not None and not timeout >= 0:
             raise ValueError("timeout must be a positive integer")
         return self._proc.wait(timeout)
 
-    # --- deprecated APIs
-
-    _locals = set(locals())
-
-    @_deprecated_method(replacement='children')
-    def get_children(self):
-        pass
-
-    @_deprecated_method(replacement='connections')
-    def get_connections(self):
-        pass
-
-    if "cpu_affinity" in _locals:
-        @_deprecated_method(replacement='cpu_affinity')
-        def get_cpu_affinity(self):
-            pass
-
-        @_deprecated_method(replacement='cpu_affinity')
-        def set_cpu_affinity(self, cpus):
-            pass
-
-    @_deprecated_method(replacement='cpu_percent')
-    def get_cpu_percent(self):
-        pass
-
-    @_deprecated_method(replacement='cpu_times')
-    def get_cpu_times(self):
-        pass
-
-    @_deprecated_method(replacement='cwd')
-    def getcwd(self):
-        pass
-
-    @_deprecated_method(replacement='memory_info_ex')
-    def get_ext_memory_info(self):
-        pass
-
-    if "io_counters" in _locals:
-        @_deprecated_method(replacement='io_counters')
-        def get_io_counters(self):
-            pass
-
-    if "ionice" in _locals:
-        @_deprecated_method(replacement='ionice')
-        def get_ionice(self):
-            pass
-
-        @_deprecated_method(replacement='ionice')
-        def set_ionice(self, ioclass, value=None):
-            pass
-
-    @_deprecated_method(replacement='memory_info')
-    def get_memory_info(self):
-        pass
-
-    @_deprecated_method(replacement='memory_maps')
-    def get_memory_maps(self):
-        pass
-
-    @_deprecated_method(replacement='memory_percent')
-    def get_memory_percent(self):
-        pass
-
-    @_deprecated_method(replacement='nice')
-    def get_nice(self):
-        pass
-
-    @_deprecated_method(replacement='num_ctx_switches')
-    def get_num_ctx_switches(self):
-        pass
-
-    if 'num_fds' in _locals:
-        @_deprecated_method(replacement='num_fds')
-        def get_num_fds(self):
-            pass
-
-    if 'num_handles' in _locals:
-        @_deprecated_method(replacement='num_handles')
-        def get_num_handles(self):
-            pass
-
-    @_deprecated_method(replacement='num_threads')
-    def get_num_threads(self):
-        pass
-
-    @_deprecated_method(replacement='open_files')
-    def get_open_files(self):
-        pass
-
-    if "rlimit" in _locals:
-        @_deprecated_method(replacement='rlimit')
-        def get_rlimit(self):
-            pass
-
-        @_deprecated_method(replacement='rlimit')
-        def set_rlimit(self, resource, limits):
-            pass
-
-    @_deprecated_method(replacement='threads')
-    def get_threads(self):
-        pass
-
-    @_deprecated_method(replacement='nice')
-    def set_nice(self, value):
-        pass
-
-    del _locals
-
 
 # =====================================================================
 # --- Popen class
 # =====================================================================
 
+
 class Popen(Process):
     """A more convenient interface to stdlib subprocess module.
     It starts a sub process and deals with it exactly as when using
     subprocess.Popen class but in addition also provides all the
     properties and methods of psutil.Process class as a unified
     interface:
 
       >>> import psutil
@@ -1224,16 +1169,17 @@ class Popen(Process):
         self.__subproc.returncode = ret
         return ret
 
 
 # =====================================================================
 # --- system processes related functions
 # =====================================================================
 
+
 def pids():
     """Return a list of current running PIDs."""
     return _psplatform.pids()
 
 
 def pid_exists(pid):
     """Return True if given PID exists in the current process list.
     This is faster than doing "pid in psutil.pids()" and
@@ -1390,23 +1336,24 @@ def wait_procs(procs, timeout=None, call
 
     return (list(gone), list(alive))
 
 
 # =====================================================================
 # --- CPU related functions
 # =====================================================================
 
+
 @memoize
 def cpu_count(logical=True):
     """Return the number of logical CPUs in the system (same as
     os.cpu_count() in Python 3.4).
 
     If logical is False return the number of physical cores only
-    (hyper thread CPUs are excluded).
+    (e.g. hyper thread CPUs are excluded).
 
     Return None if undetermined.
 
     The return value is cached after first call.
     If desired cache can be cleared like this:
 
     >>> psutil.cpu_count.cache_clear()
     """
@@ -1426,17 +1373,17 @@ def cpu_times(percpu=False):
      - nice (UNIX)
      - iowait (Linux)
      - irq (Linux, FreeBSD)
      - softirq (Linux)
      - steal (Linux >= 2.6.11)
      - guest (Linux >= 2.6.24)
      - guest_nice (Linux >= 3.2.0)
 
-    When percpu is True return a list of nameduples for each CPU.
+    When percpu is True return a list of namedtuples for each CPU.
     First element of the list refers to first CPU, second element
     to second CPU and so on.
     The order of the list is consistent across calls.
     """
     if not percpu:
         return _psplatform.cpu_times()
     else:
         return _psplatform.per_cpu_times()
@@ -1553,33 +1500,30 @@ def cpu_times_percent(interval=None, per
         all_delta = sum(t2) - sum(t1)
         for field in t1._fields:
             field_delta = getattr(t2, field) - getattr(t1, field)
             try:
                 field_perc = (100 * field_delta) / all_delta
             except ZeroDivisionError:
                 field_perc = 0.0
             field_perc = round(field_perc, 1)
-            if _WINDOWS:
-                # XXX
-                # Work around:
-                # https://github.com/giampaolo/psutil/issues/392
-                # CPU times are always supposed to increase over time
-                # or at least remain the same and that's because time
-                # cannot go backwards.
-                # Surprisingly sometimes this might not be the case on
-                # Windows where 'system' CPU time can be smaller
-                # compared to the previous call, resulting in corrupted
-                # percentages (< 0 or > 100).
-                # I really don't know what to do about that except
-                # forcing the value to 0 or 100.
-                if field_perc > 100.0:
-                    field_perc = 100.0
-                elif field_perc < 0.0:
-                    field_perc = 0.0
+            # CPU times are always supposed to increase over time
+            # or at least remain the same and that's because time
+            # cannot go backwards.
+            # Surprisingly sometimes this might not be the case (at
+            # least on Windows and Linux), see:
+            # https://github.com/giampaolo/psutil/issues/392
+            # https://github.com/giampaolo/psutil/issues/645
+            # I really don't know what to do about that except
+            # forcing the value to 0 or 100.
+            if field_perc > 100.0:
+                field_perc = 100.0
+            # `<=` because `-0.0 == 0.0` evaluates to True
+            elif field_perc <= 0.0:
+                field_perc = 0.0
             nums.append(field_perc)
         return _psplatform.scputimes(*nums)
 
     # system-wide usage
     if not percpu:
         if blocking:
             t1 = cpu_times()
             time.sleep(interval)
@@ -1600,16 +1544,17 @@ def cpu_times_percent(interval=None, per
             ret.append(calculate(t1, t2))
         return ret
 
 
 # =====================================================================
 # --- system memory related functions
 # =====================================================================
 
+
 def virtual_memory():
     """Return statistics about system memory usage as a namedtuple
     including the following fields, expressed in bytes:
 
      - total:
        total physical memory available.
 
      - available:
@@ -1680,16 +1625,17 @@ def swap_memory():
     """
     return _psplatform.swap_memory()
 
 
 # =====================================================================
 # --- disks/paritions related functions
 # =====================================================================
 
+
 def disk_usage(path):
     """Return disk usage statistics about the given path as a namedtuple
     including total, used and free space expressed in bytes plus the
     percentage usage.
     """
     return _psplatform.disk_usage(path)
 
 
@@ -1713,37 +1659,38 @@ def disk_io_counters(perdisk=False):
      - write_count: number of writes
      - read_bytes:  number of bytes read
      - write_bytes: number of bytes written
      - read_time:   time spent reading from disk (in milliseconds)
      - write_time:  time spent writing to disk (in milliseconds)
 
     If perdisk is True return the same information for every
     physical disk installed on the system as a dictionary
-    with partition names as the keys and the namedutuple
+    with partition names as the keys and the namedtuple
     described above as the values.
 
     On recent Windows versions 'diskperf -y' command may need to be
     executed first otherwise this function won't find any disk.
     """
     rawdict = _psplatform.disk_io_counters()
     if not rawdict:
         raise RuntimeError("couldn't find any physical disk")
     if perdisk:
         for disk, fields in rawdict.items():
-            rawdict[disk] = _nt_sys_diskio(*fields)
+            rawdict[disk] = _common.sdiskio(*fields)
         return rawdict
     else:
-        return _nt_sys_diskio(*[sum(x) for x in zip(*rawdict.values())])
+        return _common.sdiskio(*[sum(x) for x in zip(*rawdict.values())])
 
 
 # =====================================================================
 # --- network related functions
 # =====================================================================
 
+
 def net_io_counters(pernic=False):
     """Return network I/O statistics as a namedtuple including
     the following fields:
 
      - bytes_sent:   number of bytes sent
      - bytes_recv:   number of bytes received
      - packets_sent: number of packets sent
      - packets_recv: number of packets received
@@ -1758,20 +1705,20 @@ def net_io_counters(pernic=False):
     with network interface names as the keys and the namedtuple
     described above as the values.
     """
     rawdict = _psplatform.net_io_counters()
     if not rawdict:
         raise RuntimeError("couldn't find any network interface")
     if pernic:
         for nic, fields in rawdict.items():
-            rawdict[nic] = _nt_sys_netio(*fields)
+            rawdict[nic] = _common.snetio(*fields)
         return rawdict
     else:
-        return _nt_sys_netio(*[sum(x) for x in zip(*rawdict.values())])
+        return _common.snetio(*[sum(x) for x in zip(*rawdict.values())])
 
 
 def net_connections(kind='inet'):
     """Return system-wide connections as a list of
     (fd, family, type, laddr, raddr, status, pid) namedtuples.
     In case of limited privileges 'fd' and 'pid' may be set to -1
     and None respectively.
     The 'kind' parameter filters for connections that fit the
@@ -1784,29 +1731,85 @@ def net_connections(kind='inet'):
     tcp             TCP
     tcp4            TCP over IPv4
     tcp6            TCP over IPv6
     udp             UDP
     udp4            UDP over IPv4
     udp6            UDP over IPv6
     unix            UNIX socket (both UDP and TCP protocols)
     all             the sum of all the possible families and protocols
+
+    On OSX this function requires root privileges.
     """
     return _psplatform.net_connections(kind)
 
 
+def net_if_addrs():
+    """Return the addresses associated to each NIC (network interface
+    card) installed on the system as a dictionary whose keys are the
+    NIC names and value is a list of namedtuples for each address
+    assigned to the NIC. Each namedtuple includes 4 fields:
+
+     - family
+     - address
+     - netmask
+     - broadcast
+
+    'family' can be either socket.AF_INET, socket.AF_INET6 or
+    psutil.AF_LINK, which refers to a MAC address.
+    'address' is the primary address, 'netmask' and 'broadcast'
+    may be None.
+    Note: you can have more than one address of the same family
+    associated with each interface.
+    """
+    has_enums = sys.version_info >= (3, 4)
+    if has_enums:
+        import socket
+    rawlist = _psplatform.net_if_addrs()
+    rawlist.sort(key=lambda x: x[1])  # sort by family
+    ret = collections.defaultdict(list)
+    for name, fam, addr, mask, broadcast in rawlist:
+        if has_enums:
+            try:
+                fam = socket.AddressFamily(fam)
+            except ValueError:
+                if os.name == 'nt' and fam == -1:
+                    fam = _psplatform.AF_LINK
+                elif (hasattr(_psplatform, "AF_LINK") and
+                        _psplatform.AF_LINK == fam):
+                    # Linux defines AF_LINK as an alias for AF_PACKET.
+                    # We re-set the family here so that repr(family)
+                    # will show AF_LINK rather than AF_PACKET
+                    fam = _psplatform.AF_LINK
+        ret[name].append(_common.snic(fam, addr, mask, broadcast))
+    return dict(ret)
+
+
+def net_if_stats():
+    """Return information about each NIC (network interface card)
+    installed on the system as a dictionary whose keys are the
+    NIC names and value is a namedtuple with the following fields:
+
+     - isup: whether the interface is up (bool)
+     - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or
+               NIC_DUPLEX_UNKNOWN
+     - speed: the NIC speed expressed in mega bits (MB); if it can't
+              be determined (e.g. 'localhost') it will be set to 0.
+     - mtu: the maximum transmission unit expressed in bytes.
+    """
+    return _psplatform.net_if_stats()
+
+
 # =====================================================================
 # --- other system related functions
 # =====================================================================
 
 
 def boot_time():
-    """Return the system boot time expressed in seconds since the epoch.
-    This is also available as psutil.BOOT_TIME.
-    """
+    """Return the system boot time expressed in seconds since the epoch."""
     # Note: we are not caching this because it is subject to
     # system clock updates.
     return _psplatform.boot_time()
 
 
 def users():
     """Return users currently connected on the system as a list of
     namedtuples including the following fields.
@@ -1815,95 +1818,31 @@ def users():
      - terminal: the tty or pseudo-tty associated with the user, if any.
      - host: the host name associated with the entry, if any.
      - started: the creation time as a floating point number expressed in
        seconds since the epoch.
     """
     return _psplatform.users()
 
 
-# =====================================================================
-# --- deprecated functions
-# =====================================================================
-
-@_deprecated(replacement="psutil.pids()")
-def get_pid_list():
-    return pids()
-
-
-@_deprecated(replacement="list(process_iter())")
-def get_process_list():
-    return list(process_iter())
-
-
-@_deprecated(replacement="psutil.users()")
-def get_users():
-    return users()
-
-
-@_deprecated(replacement="psutil.virtual_memory()")
-def phymem_usage():
-    """Return the amount of total, used and free physical memory
-    on the system in bytes plus the percentage usage.
-    Deprecated; use psutil.virtual_memory() instead.
-    """
-    return virtual_memory()
-
-
-@_deprecated(replacement="psutil.swap_memory()")
-def virtmem_usage():
-    return swap_memory()
-
-
-@_deprecated(replacement="psutil.phymem_usage().free")
-def avail_phymem():
-    return phymem_usage().free
-
-
-@_deprecated(replacement="psutil.phymem_usage().used")
-def used_phymem():
-    return phymem_usage().used
-
-
-@_deprecated(replacement="psutil.virtmem_usage().total")
-def total_virtmem():
-    return virtmem_usage().total
-
-
-@_deprecated(replacement="psutil.virtmem_usage().used")
-def used_virtmem():
-    return virtmem_usage().used
-
-
-@_deprecated(replacement="psutil.virtmem_usage().free")
-def avail_virtmem():
-    return virtmem_usage().free
-
-
-@_deprecated(replacement="psutil.net_io_counters()")
-def network_io_counters(pernic=False):
-    return net_io_counters(pernic)
-
-
 def test():
     """List info of all currently running processes emulating ps aux
     output.
     """
     import datetime
-    from psutil._compat import print_
 
     today_day = datetime.date.today()
     templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s  %s"
     attrs = ['pid', 'cpu_percent', 'memory_percent', 'name', 'cpu_times',
              'create_time', 'memory_info']
     if _POSIX:
         attrs.append('uids')
         attrs.append('terminal')
-    print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
-                    "START", "TIME", "COMMAND"))
+    print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY",
+                   "START", "TIME", "COMMAND"))
     for p in process_iter():
         try:
             pinfo = p.as_dict(attrs, ad_value='')
         except NoSuchProcess:
             pass
         else:
             if pinfo['create_time']:
                 ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
@@ -1912,81 +1851,37 @@ def test():
                 else:
                     ctime = ctime.strftime("%b%d")
             else:
                 ctime = ''
             cputime = time.strftime("%M:%S",
                                     time.localtime(sum(pinfo['cpu_times'])))
             try:
                 user = p.username()
-            except KeyError:
-                if _POSIX:
-                    if pinfo['uids']:
-                        user = str(pinfo['uids'].real)
-                    else:
-                        user = ''
-                else:
-                    raise
             except Error:
                 user = ''
             if _WINDOWS and '\\' in user:
                 user = user.split('\\')[1]
             vms = pinfo['memory_info'] and \
                 int(pinfo['memory_info'].vms / 1024) or '?'
             rss = pinfo['memory_info'] and \
                 int(pinfo['memory_info'].rss / 1024) or '?'
             memp = pinfo['memory_percent'] and \
                 round(pinfo['memory_percent'], 1) or '?'
-            print_(templ % (user[:10],
-                            pinfo['pid'],
-                            pinfo['cpu_percent'],
-                            memp,
-                            vms,
-                            rss,
-                            pinfo.get('terminal', '') or '?',
-                            ctime,
-                            cputime,
-                            pinfo['name'].strip() or '?'))
+            print(templ % (
+                user[:10],
+                pinfo['pid'],
+                pinfo['cpu_percent'],
+                memp,
+                vms,
+                rss,
+                pinfo.get('terminal', '') or '?',
+                ctime,
+                cputime,
+                pinfo['name'].strip() or '?'))
 
 
-def _replace_module():
-    """Dirty hack to replace the module object in order to access
-    deprecated module constants, see:
-    http://www.dr-josiah.com/2013/12/properties-on-python-modules.html
-    """
-    class ModuleWrapper(object):
-
-        def __repr__(self):
-            return repr(self._module)
-        __str__ = __repr__
-
-        @property
-        def NUM_CPUS(self):
-            msg = "NUM_CPUS constant is deprecated; use cpu_count() instead"
-            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-            return cpu_count()
-
-        @property
-        def BOOT_TIME(self):
-            msg = "BOOT_TIME constant is deprecated; use boot_time() instead"
-            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-            return boot_time()
-
-        @property
-        def TOTAL_PHYMEM(self):
-            msg = "TOTAL_PHYMEM constant is deprecated; " \
-                  "use virtual_memory().total instead"
-            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-            return virtual_memory().total
-
-    mod = ModuleWrapper()
-    mod.__dict__ = globals()
-    mod._module = sys.modules[__name__]
-    sys.modules[__name__] = mod
-
-
-_replace_module()
-del property, memoize, division, _replace_module
+del memoize, division
 if sys.version_info < (3, 0):
     del num
 
 if __name__ == "__main__":
     test()
--- a/python/psutil/psutil/_common.py
+++ b/python/psutil/psutil/_common.py
@@ -3,29 +3,33 @@
 # 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.
 
 """Common objects shared by all _ps* modules."""
 
 from __future__ import division
 import errno
+import functools
 import os
 import socket
 import stat
 import sys
-import warnings
+from collections import namedtuple
+from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
 try:
     import threading
 except ImportError:
     import dummy_threading as threading
 
-from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
+if sys.version_info >= (3, 4):
+    import enum
+else:
+    enum = None
 
-from psutil._compat import namedtuple, wraps
 
 # --- constants
 
 AF_INET6 = getattr(socket, 'AF_INET6', None)
 AF_UNIX = getattr(socket, 'AF_UNIX', None)
 
 STATUS_RUNNING = "running"
 STATUS_SLEEPING = "sleeping"
@@ -48,16 +52,28 @@ CONN_FIN_WAIT2 = "FIN_WAIT2"
 CONN_TIME_WAIT = "TIME_WAIT"
 CONN_CLOSE = "CLOSE"
 CONN_CLOSE_WAIT = "CLOSE_WAIT"
 CONN_LAST_ACK = "LAST_ACK"
 CONN_LISTEN = "LISTEN"
 CONN_CLOSING = "CLOSING"
 CONN_NONE = "NONE"
 
+if enum is None:
+    NIC_DUPLEX_FULL = 2
+    NIC_DUPLEX_HALF = 1
+    NIC_DUPLEX_UNKNOWN = 0
+else:
+    class NicDuplex(enum.IntEnum):
+        NIC_DUPLEX_FULL = 2
+        NIC_DUPLEX_HALF = 1
+        NIC_DUPLEX_UNKNOWN = 0
+
+    globals().update(NicDuplex.__members__)
+
 
 # --- functions
 
 def usage_percent(used, total, _round=None):
     """Calculate percentage usage of 'used' against 'total'."""
     try:
         ret = (used / total) * 100
     except ZeroDivisionError:
@@ -77,17 +93,17 @@ def memoize(fun):
     ... def foo()
     ...     return 1
     ...
     >>> foo()
     1
     >>> foo.cache_clear()
     >>>
     """
-    @wraps(fun)
+    @functools.wraps(fun)
     def wrapper(*args, **kwargs):
         key = (args, frozenset(sorted(kwargs.items())))
         lock.acquire()
         try:
             try:
                 return cache[key]
             except KeyError:
                 ret = cache[key] = fun(*args, **kwargs)
@@ -104,69 +120,55 @@ def memoize(fun):
             lock.release()
 
     lock = threading.RLock()
     cache = {}
     wrapper.cache_clear = cache_clear
     return wrapper
 
 
-# http://code.activestate.com/recipes/577819-deprecated-decorator/
-def deprecated(replacement=None):
-    """A decorator which can be used to mark functions as deprecated."""
-    def outer(fun):
-        msg = "psutil.%s is deprecated" % fun.__name__
-        if replacement is not None:
-            msg += "; use %s instead" % replacement
-        if fun.__doc__ is None:
-            fun.__doc__ = msg
-
-        @wraps(fun)
-        def inner(*args, **kwargs):
-            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-            return fun(*args, **kwargs)
-
-        return inner
-    return outer
-
-
-def deprecated_method(replacement):
-    """A decorator which can be used to mark a method as deprecated
-    'replcement' is the method name which will be called instead.
-    """
-    def outer(fun):
-        msg = "%s() is deprecated; use %s() instead" % (
-            fun.__name__, replacement)
-        if fun.__doc__ is None:
-            fun.__doc__ = msg
-
-        @wraps(fun)
-        def inner(self, *args, **kwargs):
-            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-            return getattr(self, replacement)(*args, **kwargs)
-        return inner
-    return outer
-
-
 def isfile_strict(path):
     """Same as os.path.isfile() but does not swallow EACCES / EPERM
     exceptions, see:
     http://mail.python.org/pipermail/python-dev/2012-June/120787.html
     """
     try:
         st = os.stat(path)
-    except OSError:
-        err = sys.exc_info()[1]
+    except OSError as err:
         if err.errno in (errno.EPERM, errno.EACCES):
             raise
         return False
     else:
         return stat.S_ISREG(st.st_mode)
 
 
+def sockfam_to_enum(num):
+    """Convert a numeric socket family value to an IntEnum member.
+    If it's not a known member, return the numeric value itself.
+    """
+    if enum is None:
+        return num
+    try:
+        return socket.AddressFamily(num)
+    except (ValueError, AttributeError):
+        return num
+
+
+def socktype_to_enum(num):
+    """Convert a numeric socket type value to an IntEnum member.
+    If it's not a known member, return the numeric value itself.
+    """
+    if enum is None:
+        return num
+    try:
+        return socket.AddressType(num)
+    except (ValueError, AttributeError):
+        return num
+
+
 # --- Process.connections() 'kind' parameter mapping
 
 conn_tmap = {
     "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
     "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]),
     "tcp4": ([AF_INET], [SOCK_STREAM]),
     "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]),
     "udp4": ([AF_INET], [SOCK_DGRAM]),
@@ -181,17 +183,17 @@ if AF_INET6 is not None:
         "udp6": ([AF_INET6], [SOCK_DGRAM]),
     })
 
 if AF_UNIX is not None:
     conn_tmap.update({
         "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
     })
 
-del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM, socket
+del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
 
 
 # --- namedtuples for psutil.* system-related functions
 
 # psutil.swap_memory()
 sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
                              'sout'])
 # psutil.disk_usage()
@@ -207,16 +209,20 @@ snetio = namedtuple('snetio', ['bytes_se
                                'packets_sent', 'packets_recv',
                                'errin', 'errout',
                                'dropin', 'dropout'])
 # psutil.users()
 suser = namedtuple('suser', ['name', 'terminal', 'host', 'started'])
 # psutil.net_connections()
 sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
                              'status', 'pid'])
+# psutil.net_if_addrs()
+snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast'])
+# psutil.net_if_stats()
+snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
 
 
 # --- namedtuples for psutil.Process methods
 
 # psutil.Process.memory_info()
 pmem = namedtuple('pmem', ['rss', 'vms'])
 # psutil.Process.cpu_times()
 pcputimes = namedtuple('pcputimes', ['user', 'system'])
@@ -230,29 +236,11 @@ puids = namedtuple('puids', ['real', 'ef
 pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
 # psutil.Process.io_counters()
 pio = namedtuple('pio', ['read_count', 'write_count',
                          'read_bytes', 'write_bytes'])
 # psutil.Process.ionice()
 pionice = namedtuple('pionice', ['ioclass', 'value'])
 # psutil.Process.ctx_switches()
 pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
-
-
-# --- misc
-
-# backward compatibility layer for Process.connections() ntuple
-class pconn(
-    namedtuple('pconn',
-               ['fd', 'family', 'type', 'laddr', 'raddr', 'status'])):
-    __slots__ = ()
-
-    @property
-    def local_address(self):
-        warnings.warn("'local_address' field is deprecated; use 'laddr'"
-                      "instead", category=DeprecationWarning, stacklevel=2)
-        return self.laddr
-
-    @property
-    def remote_address(self):
-        warnings.warn("'remote_address' field is deprecated; use 'raddr'"
-                      "instead", category=DeprecationWarning, stacklevel=2)
-        return self.raddr
+# psutil.Process.connections()
+pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
+                             'status'])
--- a/python/psutil/psutil/_compat.py
+++ b/python/psutil/psutil/_compat.py
@@ -1,310 +1,64 @@
 #!/usr/bin/env python
 
 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
 """Module which provides compatibility with older Python versions."""
 
-__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable", "namedtuple",
-           "property", "wraps", "defaultdict", "update_wrapper", "lru_cache"]
-
+import collections
+import functools
 import sys
-try:
-    import __builtin__
-except ImportError:
-    import builtins as __builtin__  # py3
+
+__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"]
 
 PY3 = sys.version_info[0] == 3
 
 if PY3:
-    int = int
     long = int
     xrange = range
     unicode = str
-    basestring = str
-    exec_ = getattr(__builtin__, "exec")
-    print_ = getattr(__builtin__, "print")
 
     def u(s):
         return s
-
-    def b(s):
-        return s.encode("latin-1")
 else:
-    int = int
     long = long
     xrange = xrange
     unicode = unicode
-    basestring = basestring
 
     def u(s):
         return unicode(s, "unicode_escape")
 
-    def b(s):
-        return s
-
-    def exec_(code, globs=None, locs=None):
-        if globs is None:
-            frame = _sys._getframe(1)
-            globs = frame.f_globals
-            if locs is None:
-                locs = frame.f_locals
-            del frame
-        elif locs is None:
-            locs = globs
-        exec("""exec code in globs, locs""")
-
-    def print_(s):
-        sys.stdout.write(s + '\n')
-        sys.stdout.flush()
-
 
 # removed in 3.0, reintroduced in 3.2
 try:
     callable = callable
 except NameError:
     def callable(obj):
         return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
 
 
 # --- stdlib additions
 
-# py 2.6 collections.namedtuple
-# Taken from: http://code.activestate.com/recipes/500261/
-# Credits: Raymond Hettinger
-try:
-    from collections import namedtuple
-except ImportError:
-    from operator import itemgetter as _itemgetter
-    from keyword import iskeyword as _iskeyword
-    import sys as _sys
-
-    def namedtuple(typename, field_names, verbose=False, rename=False):
-        """A collections.namedtuple implementation, see:
-        http://docs.python.org/library/collections.html#namedtuple
-        """
-        if isinstance(field_names, basestring):
-            field_names = field_names.replace(',', ' ').split()
-        field_names = tuple(map(str, field_names))
-        if rename:
-            names = list(field_names)
-            seen = set()
-            for i, name in enumerate(names):
-                if ((not min(c.isalnum() or c == '_' for c in name)
-                        or _iskeyword(name)
-                        or not name or name[0].isdigit()
-                        or name.startswith('_')
-                        or name in seen)):
-                    names[i] = '_%d' % i
-                seen.add(name)
-            field_names = tuple(names)
-        for name in (typename,) + field_names:
-            if not min(c.isalnum() or c == '_' for c in name):
-                raise ValueError('Type names and field names can only contain '
-                                 'alphanumeric characters and underscores: %r'
-                                 % name)
-            if _iskeyword(name):
-                raise ValueError('Type names and field names cannot be a '
-                                 'keyword: %r' % name)
-            if name[0].isdigit():
-                raise ValueError('Type names and field names cannot start '
-                                 'with a number: %r' % name)
-        seen_names = set()
-        for name in field_names:
-            if name.startswith('_') and not rename:
-                raise ValueError(
-                    'Field names cannot start with an underscore: %r' % name)
-            if name in seen_names:
-                raise ValueError('Encountered duplicate field name: %r' % name)
-            seen_names.add(name)
-
-        numfields = len(field_names)
-        argtxt = repr(field_names).replace("'", "")[1:-1]
-        reprtxt = ', '.join('%s=%%r' % name for name in field_names)
-        template = '''class %(typename)s(tuple):
-        '%(typename)s(%(argtxt)s)' \n
-        __slots__ = () \n
-        _fields = %(field_names)r \n
-        def __new__(_cls, %(argtxt)s):
-            return _tuple.__new__(_cls, (%(argtxt)s)) \n
-        @classmethod
-        def _make(cls, iterable, new=tuple.__new__, len=len):
-            'Make a new %(typename)s object from a sequence or iterable'
-            result = new(cls, iterable)
-            if len(result) != %(numfields)d:
-                raise TypeError(
-                    'Expected %(numfields)d arguments, got %%d' %% len(result))
-            return result \n
-        def __repr__(self):
-            return '%(typename)s(%(reprtxt)s)' %% self \n
-        def _asdict(self):
-            'Return a new dict which maps field names to their values'
-            return dict(zip(self._fields, self)) \n
-        def _replace(_self, **kwds):
-            result = _self._make(map(kwds.pop, %(field_names)r, _self))
-            if kwds:
-                raise ValueError(
-                    'Got unexpected field names: %%r' %% kwds.keys())
-            return result \n
-        def __getnewargs__(self):
-            return tuple(self) \n\n''' % locals()
-        for i, name in enumerate(field_names):
-            template += '        %s = _property(_itemgetter(%d))\n' % (name, i)
-        if verbose:
-            sys.stdout.write(template + '\n')
-            sys.stdout.flush()
-
-        namespace = dict(
-            _itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
-            _property=property, _tuple=tuple)
-        try:
-            exec_(template, namespace)
-        except SyntaxError:
-            e = sys.exc_info()[1]
-            raise SyntaxError(e.message + ':\n' + template)
-        result = namespace[typename]
-        try:
-            result.__module__ = _sys._getframe(
-                1).f_globals.get('__name__', '__main__')
-        except (AttributeError, ValueError):
-            pass
-
-        return result
-
-
-# hack to support property getter/setter/deleter on python < 2.6
-# http://docs.python.org/library/functions.html?highlight=property#property
-if hasattr(property, 'setter'):
-    property = property
-else:
-    class property(__builtin__.property):
-        __metaclass__ = type
-
-        def __init__(self, fget, *args, **kwargs):
-            super(property, self).__init__(fget, *args, **kwargs)
-            self.__doc__ = fget.__doc__
-
-        def getter(self, method):
-            return property(method, self.fset, self.fdel)
-
-        def setter(self, method):
-            return property(self.fget, method, self.fdel)
-
-        def deleter(self, method):
-            return property(self.fget, self.fset, method)
-
-
-# py 2.5 collections.defauldict
-# Taken from:
-# http://code.activestate.com/recipes/523034-emulate-collectionsdefaultdict/
-# Credits: Jason Kirtland
-try:
-    from collections import defaultdict
-except ImportError:
-    class defaultdict(dict):
-        """Dict subclass that calls a factory function to supply
-        missing values:
-        http://docs.python.org/library/collections.html#collections.defaultdict
-        """
-
-        def __init__(self, default_factory=None, *a, **kw):
-            if ((default_factory is not None and
-                    not hasattr(default_factory, '__call__'))):
-                raise TypeError('first argument must be callable')
-            dict.__init__(self, *a, **kw)
-            self.default_factory = default_factory
-
-        def __getitem__(self, key):
-            try:
-                return dict.__getitem__(self, key)
-            except KeyError:
-                return self.__missing__(key)
-
-        def __missing__(self, key):
-            if self.default_factory is None:
-                raise KeyError(key)
-            self[key] = value = self.default_factory()
-            return value
-
-        def __reduce__(self):
-            if self.default_factory is None:
-                args = tuple()
-            else:
-                args = self.default_factory,
-            return type(self), args, None, None, self.items()
-
-        def copy(self):
-            return self.__copy__()
-
-        def __copy__(self):
-            return type(self)(self.default_factory, self)
-
-        def __deepcopy__(self, memo):
-            import copy
-            return type(self)(self.default_factory,
-                              copy.deepcopy(self.items()))
-
-        def __repr__(self):
-            return 'defaultdict(%s, %s)' % (self.default_factory,
-                                            dict.__repr__(self))
-
-
-# py 2.5 functools.wraps
-try:
-    from functools import wraps
-except ImportError:
-    def wraps(original):
-        def inner(fn):
-            for attribute in ['__module__', '__name__', '__doc__']:
-                setattr(fn, attribute, getattr(original, attribute))
-            for attribute in ['__dict__']:
-                if hasattr(fn, attribute):
-                    getattr(fn, attribute).update(getattr(original, attribute))
-                else:
-                    setattr(fn, attribute,
-                            getattr(original, attribute).copy())
-            return fn
-        return inner
-
-
-# py 2.5 functools.update_wrapper
-try:
-    from functools import update_wrapper
-except ImportError:
-    WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
-    WRAPPER_UPDATES = ('__dict__',)
-
-    def update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,
-                       updated=WRAPPER_UPDATES):
-        """Update a wrapper function to look like the wrapped function, see:
-        http://docs.python.org/library/functools.html#functools.update_wrapper
-        """
-        for attr in assigned:
-            setattr(wrapper, attr, getattr(wrapped, attr))
-        for attr in updated:
-            getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
-        return wrapper
-
 
 # py 3.2 functools.lru_cache
 # Taken from: http://code.activestate.com/recipes/578078
 # Credit: Raymond Hettinger
 try:
     from functools import lru_cache
 except ImportError:
     try:
         from threading import RLock
     except ImportError:
         from dummy_threading import RLock
 
-    _CacheInfo = namedtuple("CacheInfo",
-                            ["hits", "misses", "maxsize", "currsize"])
+    _CacheInfo = collections.namedtuple(
+        "CacheInfo", ["hits", "misses", "maxsize", "currsize"])
 
     class _HashedSeq(list):
         __slots__ = 'hashvalue'
 
         def __init__(self, tup, hash=hash):
             self[:] = tup
             self.hashvalue = hash(tup)
 
@@ -425,11 +179,11 @@ except ImportError:
                     root[:] = [root, root, None, None]
                     stats[:] = [0, 0]
                 finally:
                     lock.release()
 
             wrapper.__wrapped__ = user_function
             wrapper.cache_info = cache_info
             wrapper.cache_clear = cache_clear
-            return update_wrapper(wrapper, user_function)
+            return functools.update_wrapper(wrapper, user_function)
 
         return decorating_function
--- a/python/psutil/psutil/_psbsd.py
+++ b/python/psutil/psutil/_psbsd.py
@@ -2,25 +2,27 @@
 
 # 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.
 
 """FreeBSD platform implementation."""
 
 import errno
+import functools
 import os
-import sys
+import xml.etree.ElementTree as ET
+from collections import namedtuple
 
-from psutil import _common
-from psutil import _psposix
-from psutil._common import conn_tmap, usage_percent
-from psutil._compat import namedtuple, wraps
-import _psutil_bsd as cext
-import _psutil_posix
+from . import _common
+from . import _psposix
+from . import _psutil_bsd as cext
+from . import _psutil_posix as cext_posix
+from ._common import conn_tmap, usage_percent, sockfam_to_enum
+from ._common import socktype_to_enum
 
 
 __extra__all__ = []
 
 # --- constants
 
 PROC_STATUSES = {
     cext.SSTOP: _common.STATUS_STOPPED,
@@ -43,37 +45,39 @@ TCP_STATUSES = {
     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,
 }
 
 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+AF_LINK = cext_posix.AF_LINK
 
 # extend base mem ntuple with BSD-specific memory metrics
 svmem = namedtuple(
     'svmem', ['total', 'available', 'percent', 'used', 'free',
               'active', 'inactive', 'buffers', 'cached', 'shared', 'wired'])
 scputimes = namedtuple(
     'scputimes', ['user', 'nice', 'system', 'idle', 'irq'])
 pextmem = namedtuple('pextmem', ['rss', 'vms', 'text', 'data', 'stack'])
 pmmap_grouped = namedtuple(
     'pmmap_grouped', 'path rss, private, ref_count, shadow_count')
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count')
 
 # set later from __init__.py
 NoSuchProcess = None
+ZombieProcess = None
 AccessDenied = None
 TimeoutExpired = None
 
 
 def virtual_memory():
-    """System virtual memory as a namedutple."""
+    """System virtual memory as a namedtuple."""
     mem = cext.virtual_mem()
     total, free, active, inactive, wired, cached, buffers, shared = mem
     avail = inactive + cached + free
     used = active + wired + cached
     percent = usage_percent((total - avail), total, _round=1)
     return svmem(total, avail, percent, used, free,
                  active, inactive, buffers, cached, shared, wired)
 
@@ -81,24 +85,24 @@ def virtual_memory():
 def swap_memory():
     """System swap memory as (total, used, free, sin, sout) namedtuple."""
     total, used, free, sin, sout = [x * PAGESIZE for x in cext.swap_mem()]
     percent = usage_percent(used, total, _round=1)
     return _common.sswap(total, used, free, percent, sin, sout)
 
 
 def cpu_times():
-    """Return system per-CPU times as a named tuple"""
+    """Return system per-CPU times as a namedtuple"""
     user, nice, system, idle, irq = cext.cpu_times()
     return scputimes(user, nice, system, idle, irq)
 
 
 if hasattr(cext, "per_cpu_times"):
     def per_cpu_times():
-        """Return system CPU times as a named tuple"""
+        """Return system CPU times as a namedtuple"""
         ret = []
         for cpu_t in cext.per_cpu_times():
             user, nice, system, idle, irq = cpu_t
             item = scputimes(user, nice, system, idle, irq)
             ret.append(item)
         return ret
 else:
     # XXX
@@ -126,29 +130,35 @@ def cpu_count_logical():
 
 def cpu_count_physical():
     """Return the number of physical CPUs in the system."""
     # From the C module we'll get an XML string similar to this:
     # http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
     # We may get None in case "sysctl kern.sched.topology_spec"
     # is not supported on this BSD version, in which case we'll mimic
     # os.cpu_count() and return None.
+    ret = None
     s = cext.cpu_count_phys()
     if s is not None:
         # get rid of padding chars appended at the end of the string
         index = s.rfind("</groups>")
         if index != -1:
             s = s[:index + 9]
-            if sys.version_info >= (2, 5):
-                import xml.etree.ElementTree as ET
-                root = ET.fromstring(s)
-                return len(root.findall('group/children/group/cpu')) or None
-            else:
-                s = s[s.find('<children>'):]
-                return s.count("<cpu") or None
+            root = ET.fromstring(s)
+            try:
+                ret = len(root.findall('group/children/group/cpu')) or None
+            finally:
+                # needed otherwise it will memleak
+                root.clear()
+    if not ret:
+        # If logical CPUs are 1 it's obvious we'll have only 1
+        # physical CPU.
+        if cpu_count_logical() == 1:
+            return 1
+    return ret
 
 
 def boot_time():
     """The system boot time expressed in seconds since the epoch."""
     return cext.boot_time()
 
 
 def disk_partitions(all=False):
@@ -178,64 +188,90 @@ def users():
     return retlist
 
 
 def net_connections(kind):
     if kind not in _common.conn_tmap:
         raise ValueError("invalid %r kind argument; choose between %s"
                          % (kind, ', '.join([repr(x) for x in conn_tmap])))
     families, types = conn_tmap[kind]
-    ret = []
+    ret = set()
     rawlist = cext.net_connections()
     for item in rawlist:
         fd, fam, type, laddr, raddr, status, pid = item
         # TODO: apply filter at C level
         if fam in families and type in types:
-            status = TCP_STATUSES[status]
+            try:
+                status = TCP_STATUSES[status]
+            except KeyError:
+                # XXX: Not sure why this happens. I saw this occurring
+                # with IPv6 sockets opened by 'vim'. Those sockets
+                # have a very short lifetime so maybe the kernel
+                # can't initialize their status?
+                status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
+            fam = sockfam_to_enum(fam)
+            type = socktype_to_enum(type)
             nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
-            ret.append(nt)
+            ret.add(nt)
+    return list(ret)
+
+
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    names = net_io_counters().keys()
+    ret = {}
+    for name in names:
+        isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
+        if hasattr(_common, 'NicDuplex'):
+            duplex = _common.NicDuplex(duplex)
+        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
     return ret
 
 
 pids = cext.pids
 pid_exists = _psposix.pid_exists
 disk_usage = _psposix.disk_usage
 net_io_counters = cext.net_io_counters
 disk_io_counters = cext.disk_io_counters
+net_if_addrs = cext_posix.net_if_addrs
 
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError exceptions into
     NoSuchProcess and AccessDenied.
     """
-    @wraps(fun)
+    @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
-        except OSError:
+        except OSError as err:
             # support for private module import
-            if NoSuchProcess is None or AccessDenied is None:
+            if (NoSuchProcess is None or AccessDenied is None or
+                    ZombieProcess is None):
                 raise
-            err = sys.exc_info()[1]
             if err.errno == errno.ESRCH:
-                raise NoSuchProcess(self.pid, self._name)
+                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"]
+    __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
+        self._ppid = None
 
     @wrap_exceptions
     def name(self):
         return cext.proc_name(self.pid)
 
     @wrap_exceptions
     def exe(self):
         return cext.proc_exe(self.pid)
@@ -307,16 +343,18 @@ class Process(object):
         if kind not in conn_tmap:
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in conn_tmap])))
         families, types = conn_tmap[kind]
         rawlist = cext.proc_connections(self.pid, families, types)
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
+            fam = sockfam_to_enum(fam)
+            type = socktype_to_enum(type)
             status = TCP_STATUSES[status]
             nt = _common.pconn(fd, fam, type, laddr, raddr, status)
             ret.append(nt)
         return ret
 
     @wrap_exceptions
     def wait(self, timeout=None):
         try:
@@ -324,21 +362,21 @@ class Process(object):
         except _psposix.TimeoutExpired:
             # support for private module import
             if TimeoutExpired is None:
                 raise
             raise TimeoutExpired(timeout, self.pid, self._name)
 
     @wrap_exceptions
     def nice_get(self):
-        return _psutil_posix.getpriority(self.pid)
+        return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     def nice_set(self, value):
-        return _psutil_posix.setpriority(self.pid, value)
+        return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def status(self):
         code = cext.proc_status(self.pid)
         if code in PROC_STATUSES:
             return PROC_STATUSES[code]
         # XXX is this legit? will we even ever get here?
         return "?"
@@ -382,8 +420,36 @@ class Process(object):
     else:
         def _not_implemented(self):
             raise NotImplementedError("supported only starting from FreeBSD 8")
 
         open_files = _not_implemented
         proc_cwd = _not_implemented
         memory_maps = _not_implemented
         num_fds = _not_implemented
+
+    @wrap_exceptions
+    def cpu_affinity_get(self):
+        return cext.proc_cpu_affinity_get(self.pid)
+
+    @wrap_exceptions
+    def cpu_affinity_set(self, cpus):
+        # Pre-emptively check if CPUs are valid because the C
+        # function has a weird behavior in case of invalid CPUs,
+        # see: https://github.com/giampaolo/psutil/issues/586
+        allcpus = tuple(range(len(per_cpu_times())))
+        for cpu in cpus:
+            if cpu not in allcpus:
+                raise ValueError("invalid CPU #%i (choose between %s)"
+                                 % (cpu, allcpus))
+        try:
+            cext.proc_cpu_affinity_set(self.pid, cpus)
+        except OSError as err:
+            # 'man cpuset_setaffinity' about EDEADLK:
+            # <<the call would leave a thread without a valid CPU to run
+            # on because the set does not overlap with the thread's
+            # anonymous mask>>
+            if err.errno in (errno.EINVAL, errno.EDEADLK):
+                for cpu in cpus:
+                    if cpu not in allcpus:
+                        raise ValueError("invalid CPU #%i (choose between %s)"
+                                         % (cpu, allcpus))
+            raise
--- a/python/psutil/psutil/_pslinux.py
+++ b/python/psutil/psutil/_pslinux.py
@@ -5,64 +5,84 @@
 # found in the LICENSE file.
 
 """Linux platform implementation."""
 
 from __future__ import division
 
 import base64
 import errno
+import functools
 import os
 import re
 import socket
 import struct
 import sys
 import warnings
+from collections import namedtuple, defaultdict
 
-from psutil import _common
-from psutil import _psposix
-from psutil._common import (isfile_strict, usage_percent, deprecated)
-from psutil._compat import PY3, namedtuple, wraps, b, defaultdict
-import _psutil_linux as cext
-import _psutil_posix
+from . import _common
+from . import _psposix
+from . import _psutil_linux as cext
+from . import _psutil_posix as cext_posix
+from ._common import isfile_strict, usage_percent
+from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN
+from ._compat import PY3, long
+
+if sys.version_info >= (3, 4):
+    import enum
+else:
+    enum = None
 
 
 __extra__all__ = [
     # io prio constants
     "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE",
     "IOPRIO_CLASS_IDLE",
     # connection status constants
     "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
     "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
-    "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING",
-    # other
-    "phymem_buffers", "cached_phymem"]
-
+    "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ]
 
 # --- constants
 
 HAS_PRLIMIT = hasattr(cext, "linux_prlimit")
 
 # RLIMIT_* constants, not guaranteed to be present on all kernels
 if HAS_PRLIMIT:
     for name in dir(cext):
         if name.startswith('RLIM'):
             __extra__all__.append(name)
 
 # Number of clock ticks per second
 CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
 BOOT_TIME = None  # set later
 DEFAULT_ENCODING = sys.getdefaultencoding()
+if enum is None:
+    AF_LINK = socket.AF_PACKET
+else:
+    AddressFamily = enum.IntEnum('AddressFamily',
+                                 {'AF_LINK': socket.AF_PACKET})
+    AF_LINK = AddressFamily.AF_LINK
 
 # ioprio_* constants http://linux.die.net/man/2/ioprio_get
-IOPRIO_CLASS_NONE = 0
-IOPRIO_CLASS_RT = 1
-IOPRIO_CLASS_BE = 2
-IOPRIO_CLASS_IDLE = 3
+if enum is None:
+    IOPRIO_CLASS_NONE = 0
+    IOPRIO_CLASS_RT = 1
+    IOPRIO_CLASS_BE = 2
+    IOPRIO_CLASS_IDLE = 3
+else:
+    class IOPriority(enum.IntEnum):
+        IOPRIO_CLASS_NONE = 0
+        IOPRIO_CLASS_RT = 1
+        IOPRIO_CLASS_BE = 2
+        IOPRIO_CLASS_IDLE = 3
+
+    globals().update(IOPriority.__members__)
 
 # taken from /fs/proc/array.c
 PROC_STATUSES = {
     "R": _common.STATUS_RUNNING,
     "S": _common.STATUS_SLEEPING,
     "D": _common.STATUS_DISK_SLEEP,
     "T": _common.STATUS_STOPPED,
     "t": _common.STATUS_TRACING_STOP,
@@ -85,33 +105,31 @@ TCP_STATUSES = {
     "08": _common.CONN_CLOSE_WAIT,
     "09": _common.CONN_LAST_ACK,
     "0A": _common.CONN_LISTEN,
     "0B": _common.CONN_CLOSING
 }
 
 # set later from __init__.py
 NoSuchProcess = None
+ZombieProcess = None
 AccessDenied = None
 TimeoutExpired = None
 
 
 # --- named tuples
 
 def _get_cputimes_fields():
     """Return a namedtuple of variable fields depending on the
     CPU times available on this Linux kernel version which may be:
     (user, nice, system, idle, iowait, irq, softirq, [steal, [guest,
      [guest_nice]]])
     """
-    f = open('/proc/stat', 'rb')
-    try:
+    with open('/proc/stat', 'rb') as f:
         values = f.readline().split()[1:]
-    finally:
-        f.close()
     fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq']
     vlen = len(values)
     if vlen >= 8:
         # Linux >= 2.6.11
         fields.append('steal')
     if vlen >= 9:
         # Linux >= 2.6.24
         fields.append('guest')
@@ -138,223 +156,192 @@ pmmap_ext = namedtuple(
     'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
 
 
 # --- system memory
 
 def virtual_memory():
     total, free, buffers, shared, _, _ = cext.linux_sysinfo()
     cached = active = inactive = None
-    f = open('/proc/meminfo', 'rb')
-    CACHED, ACTIVE, INACTIVE = b("Cached:"), b("Active:"), b("Inactive:")
-    try:
+    with open('/proc/meminfo', 'rb') as f:
         for line in f:
-            if line.startswith(CACHED):
+            if line.startswith(b"Cached:"):
                 cached = int(line.split()[1]) * 1024
-            elif line.startswith(ACTIVE):
+            elif line.startswith(b"Active:"):
                 active = int(line.split()[1]) * 1024
-            elif line.startswith(INACTIVE):
+            elif line.startswith(b"Inactive:"):
                 inactive = int(line.split()[1]) * 1024
-            if (cached is not None
-                    and active is not None
-                    and inactive is not None):
+            if (cached is not None and
+                    active is not None and
+                    inactive is not None):
                 break
         else:
             # we might get here when dealing with exotic Linux flavors, see:
             # https://github.com/giampaolo/psutil/issues/313
             msg = "'cached', 'active' and 'inactive' memory stats couldn't " \
                   "be determined and were set to 0"
             warnings.warn(msg, RuntimeWarning)
             cached = active = inactive = 0
-    finally:
-        f.close()
     avail = free + buffers + cached
     used = total - free
     percent = usage_percent((total - avail), total, _round=1)
     return svmem(total, avail, percent, used, free,
                  active, inactive, buffers, cached)
 
 
 def swap_memory():
     _, _, _, _, total, free = cext.linux_sysinfo()
     used = total - free
     percent = usage_percent(used, total, _round=1)
     # get pgin/pgouts
-    f = open("/proc/vmstat", "rb")
-    SIN, SOUT = b('pswpin'), b('pswpout')
-    sin = sout = None
-    try:
+    with open("/proc/vmstat", "rb") as f:
+        sin = sout = None
         for line in f:
             # values are expressed in 4 kilo bytes, we want bytes instead
-            if line.startswith(SIN):
-                sin = int(line.split(b(' '))[1]) * 4 * 1024
-            elif line.startswith(SOUT):
-                sout = int(line.split(b(' '))[1]) * 4 * 1024
+            if line.startswith(b'pswpin'):
+                sin = int(line.split(b' ')[1]) * 4 * 1024
+            elif line.startswith(b'pswpout'):
+                sout = int(line.split(b' ')[1]) * 4 * 1024
             if sin is not None and sout is not None:
                 break
         else:
             # we might get here when dealing with exotic Linux flavors, see:
             # https://github.com/giampaolo/psutil/issues/313
             msg = "'sin' and 'sout' swap memory stats couldn't " \
                   "be determined and were set to 0"
             warnings.warn(msg, RuntimeWarning)
             sin = sout = 0
-    finally:
-        f.close()
     return _common.sswap(total, used, free, percent, sin, sout)
 
 
-@deprecated(replacement='psutil.virtual_memory().cached')
-def cached_phymem():
-    return virtual_memory().cached
-
-
-@deprecated(replacement='psutil.virtual_memory().buffers')
-def phymem_buffers():
-    return virtual_memory().buffers
-
-
 # --- CPUs
 
 def cpu_times():
     """Return a named tuple representing the following system-wide
     CPU times:
     (user, nice, system, idle, iowait, irq, softirq [steal, [guest,
      [guest_nice]]])
     Last 3 fields may not be available on all Linux kernel versions.
     """
-    f = open('/proc/stat', 'rb')
-    try:
+    with open('/proc/stat', 'rb') as f:
         values = f.readline().split()
-    finally:
-        f.close()
     fields = values[1:len(scputimes._fields) + 1]
     fields = [float(x) / CLOCK_TICKS for x in fields]
     return scputimes(*fields)
 
 
 def per_cpu_times():
     """Return a list of namedtuple representing the CPU times
     for every CPU available on the system.
     """
     cpus = []
-    f = open('/proc/stat', 'rb')
-    try:
+    with open('/proc/stat', 'rb') as f:
         # get rid of the first line which refers to system wide CPU stats
         f.readline()
-        CPU = b('cpu')
         for line in f:
-            if line.startswith(CPU):
+            if line.startswith(b'cpu'):
                 values = line.split()
                 fields = values[1:len(scputimes._fields) + 1]
                 fields = [float(x) / CLOCK_TICKS for x in fields]
                 entry = scputimes(*fields)
                 cpus.append(entry)
         return cpus
-    finally:
-        f.close()
 
 
 def cpu_count_logical():
     """Return the number of logical CPUs in the system."""
     try:
         return os.sysconf("SC_NPROCESSORS_ONLN")
     except ValueError:
         # as a second fallback we try to parse /proc/cpuinfo
         num = 0
-        f = open('/proc/cpuinfo', 'rb')
-        try:
-            lines = f.readlines()
-        finally:
-            f.close()
-        PROCESSOR = b('processor')
-        for line in lines:
-            if line.lower().startswith(PROCESSOR):
-                num += 1
+        with open('/proc/cpuinfo', 'rb') as f:
+            for line in f:
+                if line.lower().startswith(b'processor'):
+                    num += 1
 
-    # unknown format (e.g. amrel/sparc architectures), see:
-    # https://github.com/giampaolo/psutil/issues/200
-    # try to parse /proc/stat as a last resort
-    if num == 0:
-        f = open('/proc/stat', 'rt')
-        try:
-            lines = f.readlines()
-        finally:
-            f.close()
-        search = re.compile('cpu\d')
-        for line in lines:
-            line = line.split(' ')[0]
-            if search.match(line):
-                num += 1
+        # unknown format (e.g. amrel/sparc architectures), see:
+        # https://github.com/giampaolo/psutil/issues/200
+        # try to parse /proc/stat as a last resort
+        if num == 0:
+            search = re.compile('cpu\d')
+            with open('/proc/stat', 'rt') as f:
+                for line in f:
+                    line = line.split(' ')[0]
+                    if search.match(line):
+                        num += 1
 
-    if num == 0:
-        # mimic os.cpu_count()
-        return None
-    return num
+        if num == 0:
+            # mimic os.cpu_count()
+            return None
+        return num
 
 
 def cpu_count_physical():
-    """Return the number of physical CPUs in the system."""
-    f = open('/proc/cpuinfo', 'rb')
-    try:
-        lines = f.readlines()
-    finally:
-        f.close()
-    found = set()
-    PHYSICAL_ID = b('physical id')
-    for line in lines:
-        if line.lower().startswith(PHYSICAL_ID):
-            found.add(line.strip())
-    if found:
-        return len(found)
-    else:
-        return None  # mimic os.cpu_count()
+    """Return the number of physical cores in the system."""
+    mapping = {}
+    current_info = {}
+    with open('/proc/cpuinfo', 'rb') as f:
+        for line in f:
+            line = line.strip().lower()
+            if not line:
+                # new section
+                if (b'physical id' in current_info and
+                        b'cpu cores' in current_info):
+                    mapping[current_info[b'physical id']] = \
+                        current_info[b'cpu cores']
+                current_info = {}
+            else:
+                # ongoing section
+                if (line.startswith(b'physical id') or
+                        line.startswith(b'cpu cores')):
+                    key, value = line.split(b'\t:', 1)
+                    current_info[key] = int(value)
+
+    # mimic os.cpu_count()
+    return sum(mapping.values()) or None
 
 
 # --- other system functions
 
 def users():
     """Return currently connected users as a list of namedtuples."""
     retlist = []
     rawlist = cext.users()
     for item in rawlist:
         user, tty, hostname, tstamp, user_process = 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 == ':0.0':
+        if hostname == ':0.0' or hostname == ':0':
             hostname = 'localhost'
         nt = _common.suser(user, tty or None, hostname, tstamp)
         retlist.append(nt)
     return retlist
 
 
 def boot_time():
     """Return the system boot time expressed in seconds since the epoch."""
     global BOOT_TIME
-    f = open('/proc/stat', 'rb')
-    try:
-        BTIME = b('btime')
+    with open('/proc/stat', 'rb') as f:
         for line in f:
-            if line.startswith(BTIME):
+            if line.startswith(b'btime'):
                 ret = float(line.strip().split()[1])
                 BOOT_TIME = ret
                 return ret
-        raise RuntimeError("line 'btime' not found")
-    finally:
-        f.close()
+        raise RuntimeError("line 'btime' not found in /proc/stat")
 
 
 # --- processes
 
 def pids():
     """Returns a list of PIDs currently running on the system."""
-    return [int(x) for x in os.listdir(b('/proc')) if x.isdigit()]
+    return [int(x) for x in os.listdir(b'/proc') if x.isdigit()]
 
 
 def pid_exists(pid):
     """Check For the existence of a unix pid."""
     return _psposix.pid_exists(pid)
 
 
 # --- network
@@ -391,40 +378,47 @@ class Connections:
             "inet6": (tcp6, udp6),
         }
 
     def get_proc_inodes(self, pid):
         inodes = defaultdict(list)
         for fd in os.listdir("/proc/%s/fd" % pid):
             try:
                 inode = os.readlink("/proc/%s/fd/%s" % (pid, fd))
-            except OSError:
-                # TODO: need comment here
-                continue
+            except OSError as err:
+                # ENOENT == file which is gone in the meantime;
+                # os.stat('/proc/%s' % self.pid) will be done later
+                # to force NSP (if it's the case)
+                if err.errno in (errno.ENOENT, errno.ESRCH):
+                    continue
+                elif err.errno == errno.EINVAL:
+                    # not a link
+                    continue
+                else:
+                    raise
             else:
                 if inode.startswith('socket:['):
                     # the process is using a socket
                     inode = inode[8:][:-1]
                     inodes[inode].append((pid, int(fd)))
         return inodes
 
     def get_all_inodes(self):
         inodes = {}
         for pid in pids():
             try:
                 inodes.update(self.get_proc_inodes(pid))
-            except OSError:
+            except OSError as err:
                 # os.listdir() is gonna raise a lot of access denied
                 # exceptions in case of unprivileged user; that's fine
                 # as we'll just end up returning a connection with PID
                 # and fd set to None anyway.
                 # Both netstat -an and lsof does the same so it's
                 # unlikely we can do any better.
                 # ENOENT just means a PID disappeared on us.
-                err = sys.exc_info()[1]
                 if err.errno not in (
                         errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES):
                     raise
         return inodes
 
     def decode_address(self, addr, family):
         """Accept an "ip:port" address as displayed in /proc/net/*
         and convert it into a human readable form, like:
@@ -472,118 +466,122 @@ class Connections:
                     struct.pack('<4I', *struct.unpack('<4I', ip)))
         return (ip, port)
 
     def process_inet(self, file, family, type_, inodes, filter_pid=None):
         """Parse /proc/net/tcp* and /proc/net/udp* files."""
         if file.endswith('6') and not os.path.exists(file):
             # IPv6 not supported
             return
-        f = open(file, 'rt')
-        f.readline()  # skip the first line
-        for line in f:
-            _, laddr, raddr, status, _, _, _, _, _, inode = \
-                line.split()[:10]
-            if inode in inodes:
-                # We assume inet sockets are unique, so we error
-                # out if there are multiple references to the
-                # same inode. We won't do this for UNIX sockets.
-                if len(inodes[inode]) > 1 and type_ != socket.AF_UNIX:
-                    raise ValueError("ambiguos inode with multiple "
-                                     "PIDs references")
-                pid, fd = inodes[inode][0]
-            else:
-                pid, fd = None, -1
-            if filter_pid is not None and filter_pid != pid:
-                continue
-            else:
-                if type_ == socket.SOCK_STREAM:
-                    status = TCP_STATUSES[status]
+        with open(file, 'rt') as f:
+            f.readline()  # skip the first line
+            for line in f:
+                try:
+                    _, laddr, raddr, status, _, _, _, _, _, inode = \
+                        line.split()[:10]
+                except ValueError:
+                    raise RuntimeError(
+                        "error while parsing %s; malformed line %r" % (
+                            file, line))
+                if inode in inodes:
+                    # # We assume inet sockets are unique, so we error
+                    # # out if there are multiple references to the
+                    # # same inode. We won't do this for UNIX sockets.
+                    # if len(inodes[inode]) > 1 and family != socket.AF_UNIX:
+                    #     raise ValueError("ambiguos inode with multiple "
+                    #                      "PIDs references")
+                    pid, fd = inodes[inode][0]
                 else:
-                    status = _common.CONN_NONE
-                laddr = self.decode_address(laddr, family)
-                raddr = self.decode_address(raddr, family)
-                yield (fd, family, type_, laddr, raddr, status, pid)
-        f.close()
+                    pid, fd = None, -1
+                if filter_pid is not None and filter_pid != pid:
+                    continue
+                else:
+                    if type_ == socket.SOCK_STREAM:
+                        status = TCP_STATUSES[status]
+                    else:
+                        status = _common.CONN_NONE
+                    laddr = self.decode_address(laddr, family)
+                    raddr = self.decode_address(raddr, family)
+                    yield (fd, family, type_, laddr, raddr, status, pid)
 
     def process_unix(self, file, family, inodes, filter_pid=None):
         """Parse /proc/net/unix files."""
-        f = open(file, 'rt')
-        f.readline()  # skip the first line
-        for line in f:
-            tokens = line.split()
-            _, _, _, _, type_, _, inode = tokens[0:7]
-            if inode in inodes:
-                # With UNIX sockets we can have a single inode
-                # referencing many file descriptors.
-                pairs = inodes[inode]
-            else:
-                pairs = [(None, -1)]
-            for pid, fd in pairs:
-                if filter_pid is not None and filter_pid != pid:
-                    continue
+        with open(file, 'rt') as f:
+            f.readline()  # skip the first line
+            for line in f:
+                tokens = line.split()
+                try:
+                    _, _, _, _, type_, _, inode = tokens[0:7]
+                except ValueError:
+                    raise RuntimeError(
+                        "error while parsing %s; malformed line %r" % (
+                            file, line))
+                if inode in inodes:
+                    # With UNIX sockets we can have a single inode
+                    # referencing many file descriptors.
+                    pairs = inodes[inode]
                 else:
-                    if len(tokens) == 8:
-                        path = tokens[-1]
+                    pairs = [(None, -1)]
+                for pid, fd in pairs:
+                    if filter_pid is not None and filter_pid != pid:
+                        continue
                     else:
-                        path = ""
-                    type_ = int(type_)
-                    raddr = None
-                    status = _common.CONN_NONE
-                    yield (fd, family, type_, path, raddr, status, pid)
-        f.close()
+                        if len(tokens) == 8:
+                            path = tokens[-1]
+                        else:
+                            path = ""
+                        type_ = int(type_)
+                        raddr = None
+                        status = _common.CONN_NONE
+                        yield (fd, family, type_, path, raddr, status, pid)
 
     def retrieve(self, kind, pid=None):
         if kind not in self.tmap:
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in self.tmap])))
         if pid is not None:
             inodes = self.get_proc_inodes(pid)
             if not inodes:
                 # no connections for this process
                 return []
         else:
             inodes = self.get_all_inodes()
-        ret = []
+        ret = set()
         for f, family, type_ in self.tmap[kind]:
             if family in (socket.AF_INET, socket.AF_INET6):
                 ls = self.process_inet(
                     "/proc/net/%s" % f, family, type_, inodes, filter_pid=pid)
             else:
                 ls = self.process_unix(
                     "/proc/net/%s" % f, family, inodes, filter_pid=pid)
             for fd, family, type_, laddr, raddr, status, bound_pid in ls:
                 if pid:
                     conn = _common.pconn(fd, family, type_, laddr, raddr,
                                          status)
                 else:
                     conn = _common.sconn(fd, family, type_, laddr, raddr,
                                          status, bound_pid)
-                ret.append(conn)
-        return ret
+                ret.add(conn)
+        return list(ret)
 
 
 _connections = Connections()
 
 
 def net_connections(kind='inet'):
     """Return system-wide open connections."""
     return _connections.retrieve(kind)
 
 
 def net_io_counters():
     """Return network I/O statistics for every network interface
     installed on the system as a dict of raw tuples.
     """
-    f = open("/proc/net/dev", "rt")
-    try:
+    with open("/proc/net/dev", "rt") as f:
         lines = f.readlines()
-    finally:
-        f.close()
-
     retdict = {}
     for line in lines[2:]:
         colon = line.rfind(':')
         assert colon > 0, repr(line)
         name = line[:colon].strip()
         fields = line[colon + 1:].strip().split()
         bytes_recv = int(fields[0])
         packets_recv = int(fields[1])
@@ -593,54 +591,65 @@ def net_io_counters():
         packets_sent = int(fields[9])
         errout = int(fields[10])
         dropout = int(fields[11])
         retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv,
                          errin, errout, dropin, dropout)
     return retdict
 
 
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL,
+                  cext.DUPLEX_HALF: NIC_DUPLEX_HALF,
+                  cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN}
+    names = net_io_counters().keys()
+    ret = {}
+    for name in names:
+        isup, duplex, speed, mtu = cext.net_if_stats(name)
+        duplex = duplex_map[duplex]
+        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+    return ret
+
+
+net_if_addrs = cext_posix.net_if_addrs
+
+
 # --- disks
 
 def disk_io_counters():
     """Return disk I/O statistics for every disk installed on the
     system as a dict of raw tuples.
     """
     # man iostat states that sectors are equivalent with blocks and
     # have a size of 512 bytes since 2.4 kernels. This value is
     # needed to calculate the amount of disk I/O in bytes.
     SECTOR_SIZE = 512
 
     # determine partitions we want to look for
     partitions = []
-    f = open("/proc/partitions", "rt")
-    try:
+    with open("/proc/partitions", "rt") as f:
         lines = f.readlines()[2:]
-    finally:
-        f.close()
     for line in reversed(lines):
         _, _, _, name = line.split()
         if name[-1].isdigit():
             # we're dealing with a partition (e.g. 'sda1'); 'sda' will
             # also be around but we want to omit it
             partitions.append(name)
         else:
             if not partitions or not partitions[-1].startswith(name):
                 # we're dealing with a disk entity for which no
                 # partitions have been defined (e.g. 'sda' but
                 # 'sda1' was not around), see:
                 # https://github.com/giampaolo/psutil/issues/338
                 partitions.append(name)
     #
     retdict = {}
-    f = open("/proc/diskstats", "rt")
-    try:
+    with open("/proc/diskstats", "rt") as f:
         lines = f.readlines()
-    finally:
-        f.close()
     for line in lines:
         # http://www.mjmwired.net/kernel/Documentation/iostats.txt
         fields = line.split()
         if len(fields) > 7:
             _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \
                 fields[:11]
         else:
             # from kernel 2.6.0 to 2.6.25
@@ -653,106 +662,119 @@ def disk_io_counters():
             writes = int(writes)
             rtime = int(rtime)
             wtime = int(wtime)
             retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime)
     return retdict
 
 
 def disk_partitions(all=False):
-    """Return mounted disk partitions as a list of nameduples"""
-    phydevs = []
-    f = open("/proc/filesystems", "r")
-    try:
+    """Return mounted disk partitions as a list of namedtuples"""
+    fstypes = set()
+    with open("/proc/filesystems", "r") as f:
         for line in f:
+            line = line.strip()
             if not line.startswith("nodev"):
-                phydevs.append(line.strip())
-    finally:
-        f.close()
+                fstypes.add(line.strip())
+            else:
+                # ignore all lines starting with "nodev" except "nodev zfs"
+                fstype = line.split("\t")[1]
+                if fstype == "zfs":
+                    fstypes.add("zfs")
 
     retlist = []
     partitions = cext.disk_partitions()
     for partition in partitions:
         device, mountpoint, fstype, opts = partition
         if device == 'none':
             device = ''
         if not all:
-            if device == '' or fstype not in phydevs:
+            if device == '' or fstype not in fstypes:
                 continue
         ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
         retlist.append(ntuple)
     return retlist
 
 
 disk_usage = _psposix.disk_usage
 
 
 # --- decorators
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError and IOError exceptions
     into NoSuchProcess and AccessDenied.
     """
-    @wraps(fun)
+    @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
-        except EnvironmentError:
+        except EnvironmentError as err:
             # support for private module import
             if NoSuchProcess is None or AccessDenied 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.
-            err = sys.exc_info()[1]
             if err.errno in (errno.ENOENT, errno.ESRCH):
                 raise NoSuchProcess(self.pid, self._name)
             if err.errno in (errno.EPERM, errno.EACCES):
                 raise AccessDenied(self.pid, self._name)
             raise
     return wrapper
 
 
+def wrap_exceptions_w_zombie(fun):
+    """Same as above but also handles zombies."""
+    @functools.wraps(fun)
+    def wrapper(self, *args, **kwargs):
+        try:
+            return wrap_exceptions(fun)(self)
+        except NoSuchProcess:
+            if not pid_exists(self.pid):
+                raise
+            else:
+                raise ZombieProcess(self.pid, self._name, self._ppid)
+    return wrapper
+
+
 class Process(object):
     """Linux process implementation."""
 
-    __slots__ = ["pid", "_name"]
+    __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
+        self._ppid = None
 
     @wrap_exceptions
     def name(self):
         fname = "/proc/%s/stat" % self.pid
-        if PY3:
-            f = open(fname, "rt", encoding=DEFAULT_ENCODING)
-        else:
-            f = open(fname, "rt")
-        try:
-            name = f.read().split(' ')[1].replace('(', '').replace(')', '')
-        finally:
-            f.close()
+        kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict()
+        with open(fname, "rt", **kw) as f:
+            data = f.read()
         # XXX - gets changed later and probably needs refactoring
-        return name
+        return data[data.find('(') + 1:data.rfind(')')]
 
     def exe(self):
         try:
             exe = os.readlink("/proc/%s/exe" % self.pid)
-        except (OSError, IOError):
-            err = sys.exc_info()[1]
+        except OSError as err:
             if err.errno in (errno.ENOENT, errno.ESRCH):
                 # no such file error; might be raised also if the
                 # path actually exists for system processes with
                 # low pids (about 0-20)
                 if os.path.lexists("/proc/%s" % self.pid):
                     return ""
                 else:
-                    # ok, it is a process which has gone away
-                    raise NoSuchProcess(self.pid, self._name)
+                    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
 
         # readlink() might return paths containing null bytes ('\x00').
         # Certain names have ' (deleted)' appended. Usually this is
         # bogus as the file actually exists. Either way that's not
         # important as we don't want to discriminate executables which
@@ -760,151 +782,128 @@ class Process(object):
         exe = exe.split('\x00')[0]
         if exe.endswith(' (deleted)') and not os.path.exists(exe):
             exe = exe[:-10]
         return exe
 
     @wrap_exceptions
     def cmdline(self):
         fname = "/proc/%s/cmdline" % self.pid
-        if PY3:
-            f = open(fname, "rt", encoding=DEFAULT_ENCODING)
-        else:
-            f = open(fname, "rt")
-        try:
-            # return the args as a list
-            return [x for x in f.read().split('\x00') if x]
-        finally:
-            f.close()
+        kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict()
+        with open(fname, "rt", **kw) as f:
+            data = f.read()
+        if data.endswith('\x00'):
+            data = data[:-1]
+        return [x for x in data.split('\x00')]
 
     @wrap_exceptions
     def terminal(self):
         tmap = _psposix._get_terminal_map()
-        f = open("/proc/%s/stat" % self.pid, 'rb')
-        try:
-            tty_nr = int(f.read().split(b(' '))[6])
-        finally:
-            f.close()
+        with open("/proc/%s/stat" % self.pid, 'rb') as f:
+            tty_nr = int(f.read().split(b' ')[6])
         try:
             return tmap[tty_nr]
         except KeyError:
             return None
 
     if os.path.exists('/proc/%s/io' % os.getpid()):
         @wrap_exceptions
         def io_counters(self):
             fname = "/proc/%s/io" % self.pid
-            f = open(fname, 'rb')
-            SYSCR, SYSCW = b("syscr"), b("syscw")
-            READ_BYTES, WRITE_BYTES = b("read_bytes"), b("write_bytes")
-            try:
+            with open(fname, 'rb') as f:
                 rcount = wcount = rbytes = wbytes = None
                 for line in f:
-                    if rcount is None and line.startswith(SYSCR):
+                    if rcount is None and line.startswith(b"syscr"):
                         rcount = int(line.split()[1])
-                    elif wcount is None and line.startswith(SYSCW):
+                    elif wcount is None and line.startswith(b"syscw"):
                         wcount = int(line.split()[1])
-                    elif rbytes is None and line.startswith(READ_BYTES):
+                    elif rbytes is None and line.startswith(b"read_bytes"):
                         rbytes = int(line.split()[1])
-                    elif wbytes is None and line.startswith(WRITE_BYTES):
+                    elif wbytes is None and line.startswith(b"write_bytes"):
                         wbytes = int(line.split()[1])
                 for x in (rcount, wcount, rbytes, wbytes):
                     if x is None:
                         raise NotImplementedError(
                             "couldn't read all necessary info from %r" % fname)
                 return _common.pio(rcount, wcount, rbytes, wbytes)
-            finally:
-                f.close()
     else:
         def io_counters(self):
             raise NotImplementedError("couldn't find /proc/%s/io (kernel "
                                       "too old?)" % self.pid)
 
     @wrap_exceptions
     def cpu_times(self):
-        f = open("/proc/%s/stat" % self.pid, 'rb')
-        try:
+        with open("/proc/%s/stat" % self.pid, 'rb') as f:
             st = f.read().strip()
-        finally:
-            f.close()
         # ignore the first two values ("pid (exe)")
-        st = st[st.find(b(')')) + 2:]
-        values = st.split(b(' '))
+        st = st[st.find(b')') + 2:]
+        values = st.split(b' ')
         utime = float(values[11]) / CLOCK_TICKS
         stime = float(values[12]) / CLOCK_TICKS
         return _common.pcputimes(utime, stime)
 
     @wrap_exceptions
     def wait(self, timeout=None):
         try:
             return _psposix.wait_pid(self.pid, timeout)
         except _psposix.TimeoutExpired:
             # support for private module import
             if TimeoutExpired is None:
                 raise
             raise TimeoutExpired(timeout, self.pid, self._name)
 
     @wrap_exceptions
     def create_time(self):
-        f = open("/proc/%s/stat" % self.pid, 'rb')
-        try:
+        with open("/proc/%s/stat" % self.pid, 'rb') as f:
             st = f.read().strip()
-        finally:
-            f.close()
         # ignore the first two values ("pid (exe)")
-        st = st[st.rfind(b(')')) + 2:]
-        values = st.split(b(' '))
+        st = st[st.rfind(b')') + 2:]
+        values = st.split(b' ')
         # According to documentation, starttime is in field 21 and the
         # unit is jiffies (clock ticks).
         # We first divide it for clock ticks and then add uptime returning
         # seconds since the epoch, in UTC.
         # Also use cached value if available.
         bt = BOOT_TIME or boot_time()
         return (float(values[19]) / CLOCK_TICKS) + bt
 
     @wrap_exceptions
     def memory_info(self):
-        f = open("/proc/%s/statm" % self.pid, 'rb')
-        try:
+        with open("/proc/%s/statm" % self.pid, 'rb') as f:
             vms, rss = f.readline().split()[:2]
             return _common.pmem(int(rss) * PAGESIZE,
                                 int(vms) * PAGESIZE)
-        finally:
-            f.close()
 
     @wrap_exceptions
     def memory_info_ex(self):
         #  ============================================================
         # | FIELD  | DESCRIPTION                         | AKA  | TOP  |
         #  ============================================================
         # | rss    | resident set size                   |      | RES  |
         # | vms    | total program size                  | size | VIRT |
         # | shared | shared pages (from shared mappings) |      | SHR  |
         # | text   | text ('code')                       | trs  | CODE |
         # | lib    | library (unused in Linux 2.6)       | lrs  |      |
         # | data   | data + stack                        | drs  | DATA |
         # | dirty  | dirty pages (unused in Linux 2.6)   | dt   |      |
         #  ============================================================
-        f = open("/proc/%s/statm" % self.pid, "rb")
-        try:
+        with open("/proc/%s/statm" % self.pid, "rb") as f:
             vms, rss, shared, text, lib, data, dirty = \
                 [int(x) * PAGESIZE for x in f.readline().split()[:7]]
-        finally:
-            f.close()
         return pextmem(rss, vms, shared, text, lib, data, dirty)
 
     if os.path.exists('/proc/%s/smaps' % os.getpid()):
+
+        @wrap_exceptions
         def memory_maps(self):
-            """Return process's mapped memory regions as a list of nameduples.
+            """Return process's mapped memory regions as a list of named tuples.
             Fields are explained in 'man proc'; here is an updated (Apr 2012)
             version: http://goo.gl/fmebo
             """
-            f = None
-            try:
-                f = open("/proc/%s/smaps" % self.pid, "rt")
+            with open("/proc/%s/smaps" % self.pid, "rt") as f:
                 first_line = f.readline()
                 current_block = [first_line]
 
                 def get_blocks():
                     data = {}
                     for line in f:
                         fields = line.split(None, 5)
                         if not fields[0].endswith(':'):
@@ -918,263 +917,253 @@ class Process(object):
                                 if fields[0].startswith('VmFlags:'):
                                     # see issue #369
                                     continue
                                 else:
                                     raise ValueError("don't know how to inte"
                                                      "rpret line %r" % line)
                     yield (current_block.pop(), data)
 
+                ls = []
                 if first_line:  # smaps file can be empty
                     for header, data in get_blocks():
                         hfields = header.split(None, 5)
                         try:
                             addr, perms, offset, dev, inode, path = hfields
                         except ValueError:
                             addr, perms, offset, dev, inode, path = \
                                 hfields + ['']
                         if not path:
                             path = '[anon]'
                         else:
                             path = path.strip()
-                        yield (addr, perms, path,
-                               data['Rss:'],
-                               data.get('Size:', 0),
-                               data.get('Pss:', 0),
-                               data.get('Shared_Clean:', 0),
-                               data.get('Shared_Dirty:', 0),
-                               data.get('Private_Clean:', 0),
-                               data.get('Private_Dirty:', 0),
-                               data.get('Referenced:', 0),
-                               data.get('Anonymous:', 0),
-                               data.get('Swap:', 0))
-                f.close()
-            except EnvironmentError:
-                # XXX - Can't use wrap_exceptions decorator as we're
-                # returning a generator;  this probably needs some
-                # refactoring in order to avoid this code duplication.
-                if f is not None:
-                    f.close()
-                err = sys.exc_info()[1]
-                if err.errno in (errno.ENOENT, errno.ESRCH):
-                    raise NoSuchProcess(self.pid, self._name)
-                if err.errno in (errno.EPERM, errno.EACCES):
-                    raise AccessDenied(self.pid, self._name)
-                raise
-            except:
-                if f is not None:
-                    f.close()
-                raise
-            f.close()
+                        ls.append((
+                            addr, perms, path,
+                            data['Rss:'],
+                            data.get('Size:', 0),
+                            data.get('Pss:', 0),
+                            data.get('Shared_Clean:', 0),
+                            data.get('Shared_Dirty:', 0),
+                            data.get('Private_Clean:', 0),
+                            data.get('Private_Dirty:', 0),
+                            data.get('Referenced:', 0),
+                            data.get('Anonymous:', 0),
+                            data.get('Swap:', 0)
+                        ))
+            return ls
 
     else:
         def memory_maps(self):
             msg = "couldn't find /proc/%s/smaps; kernel < 2.6.14 or "  \
                   "CONFIG_MMU kernel configuration option is not enabled" \
                   % self.pid
             raise NotImplementedError(msg)
 
-    @wrap_exceptions
+    @wrap_exceptions_w_zombie
     def cwd(self):
         # readlink() might return paths containing null bytes causing
         # problems when used with other fs-related functions (os.*,
         # open(), ...)
         path = os.readlink("/proc/%s/cwd" % self.pid)
         return path.replace('\x00', '')
 
     @wrap_exceptions
     def num_ctx_switches(self):
         vol = unvol = None
-        f = open("/proc/%s/status" % self.pid, "rb")
-        VOLUNTARY = b("voluntary_ctxt_switches")
-        NON_VOLUNTARY = b("nonvoluntary_ctxt_switches")
-        try:
+        with open("/proc/%s/status" % self.pid, "rb") as f:
             for line in f:
-                if line.startswith(VOLUNTARY):
+                if line.startswith(b"voluntary_ctxt_switches"):
                     vol = int(line.split()[1])
-                elif line.startswith(NON_VOLUNTARY):
+                elif line.startswith(b"nonvoluntary_ctxt_switches"):
                     unvol = int(line.split()[1])
                 if vol is not None and unvol is not None:
                     return _common.pctxsw(vol, unvol)
             raise NotImplementedError(
                 "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'"
                 "fields were not found in /proc/%s/status; the kernel is "
                 "probably older than 2.6.23" % self.pid)
-        finally:
-            f.close()
 
     @wrap_exceptions
     def num_threads(self):
-        f = open("/proc/%s/status" % self.pid, "rb")
-        try:
-            THREADS = b("Threads:")
+        with open("/proc/%s/status" % self.pid, "rb") as f:
             for line in f:
-                if line.startswith(THREADS):
+                if line.startswith(b"Threads:"):
                     return int(line.split()[1])
             raise NotImplementedError("line not found")
-        finally:
-            f.close()
 
     @wrap_exceptions
     def threads(self):
         thread_ids = os.listdir("/proc/%s/task" % self.pid)
         thread_ids.sort()
         retlist = []
         hit_enoent = False
         for thread_id in thread_ids:
+            fname = "/proc/%s/task/%s/stat" % (self.pid, thread_id)
             try:
-                f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id), 'rb')
-            except EnvironmentError:
-                err = sys.exc_info()[1]
+                with open(fname, 'rb') as f:
+                    st = f.read().strip()
+            except IOError as err:
                 if err.errno == errno.ENOENT:
                     # no such file or directory; it means thread
                     # disappeared on us
                     hit_enoent = True
                     continue
                 raise
-            try:
-                st = f.read().strip()
-            finally:
-                f.close()
             # ignore the first two values ("pid (exe)")
-            st = st[st.find(b(')')) + 2:]
-            values = st.split(b(' '))
+            st = st[st.find(b')') + 2:]
+            values = st.split(b' ')
             utime = float(values[11]) / CLOCK_TICKS
             stime = float(values[12]) / CLOCK_TICKS
             ntuple = _common.pthread(int(thread_id), utime, stime)
             retlist.append(ntuple)
         if hit_enoent:
             # raise NSP if the process disappeared on us
             os.stat('/proc/%s' % self.pid)
         return retlist
 
     @wrap_exceptions
     def nice_get(self):
-        # f = open('/proc/%s/stat' % self.pid, 'r')
-        # try:
+        # with open('/proc/%s/stat' % self.pid, 'r') as f:
         #   data = f.read()
         #   return int(data.split()[18])
-        # finally:
-        #   f.close()
 
         # Use C implementation
-        return _psutil_posix.getpriority(self.pid)
+        return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     def nice_set(self, value):
-        return _psutil_posix.setpriority(self.pid, value)
+        return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def cpu_affinity_get(self):
         return cext.proc_cpu_affinity_get(self.pid)
 
     @wrap_exceptions
     def cpu_affinity_set(self, cpus):
         try:
             cext.proc_cpu_affinity_set(self.pid, cpus)
-        except OSError:
-            err = sys.exc_info()[1]
+        except OSError as err:
             if err.errno == errno.EINVAL:
                 allcpus = tuple(range(len(per_cpu_times())))
                 for cpu in cpus:
                     if cpu not in allcpus:
                         raise ValueError("invalid CPU #%i (choose between %s)"
                                          % (cpu, allcpus))
             raise
 
     # only starting from kernel 2.6.13
     if hasattr(cext, "proc_ioprio_get"):
 
         @wrap_exceptions
         def ionice_get(self):
             ioclass, value = cext.proc_ioprio_get(self.pid)
+            if enum is not None:
+                ioclass = IOPriority(ioclass)
             return _common.pionice(ioclass, value)
 
         @wrap_exceptions
         def ionice_set(self, ioclass, value):
+            if value is not None:
+                if not PY3 and not isinstance(value, (int, long)):
+                    msg = "value argument is not an integer (gor %r)" % value
+                    raise TypeError(msg)
+                if not 0 <= value <= 8:
+                    raise ValueError(
+                        "value argument range expected is between 0 and 8")
+
             if ioclass in (IOPRIO_CLASS_NONE, None):
                 if value:
-                    msg = "can't specify value with IOPRIO_CLASS_NONE"
+                    msg = "can't specify value with IOPRIO_CLASS_NONE " \
+                          "(got %r)" % value
                     raise ValueError(msg)
                 ioclass = IOPRIO_CLASS_NONE
                 value = 0
-            if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE):
-                if value is None:
-                    value = 4
             elif ioclass == IOPRIO_CLASS_IDLE:
                 if value:
-                    msg = "can't specify value with IOPRIO_CLASS_IDLE"
+                    msg = "can't specify value with IOPRIO_CLASS_IDLE " \
+                          "(got %r)" % value
                     raise ValueError(msg)
                 value = 0
+            elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE):
+                if value is None:
+                    # TODO: add comment explaining why this is 4 (?)
+                    value = 4
             else:
-                value = 0
-            if not 0 <= value <= 8:
-                raise ValueError(
-                    "value argument range expected is between 0 and 8")
+                # otherwise we would get OSError(EVINAL)
+                raise ValueError("invalid ioclass argument %r" % ioclass)
+
             return cext.proc_ioprio_set(self.pid, ioclass, value)
 
     if HAS_PRLIMIT:
         @wrap_exceptions
         def rlimit(self, resource, limits=None):
-            # if pid is 0 prlimit() applies to the calling process and
-            # we don't want that
+            # If pid is 0 prlimit() applies to the calling process and
+            # we don't want that. We should never get here though as
+            # PID 0 is not supported on Linux.
             if self.pid == 0:
                 raise ValueError("can't use prlimit() against PID 0 process")
-            if limits is None:
-                # get
-                return cext.linux_prlimit(self.pid, resource)
-            else:
-                # set
-                if len(limits) != 2:
-                    raise ValueError(
-                        "second argument must be a (soft, hard) tuple")
-                soft, hard = limits
-                cext.linux_prlimit(self.pid, resource, soft, hard)
+            try:
+                if limits is None:
+                    # get
+                    return cext.linux_prlimit(self.pid, resource)
+                else:
+                    # set
+                    if len(limits) != 2:
+                        raise ValueError(
+                            "second argument must be a (soft, hard) tuple, "
+                            "got %s" % repr(limits))
+                    soft, hard = limits
+                    cext.linux_prlimit(self.pid, resource, soft, hard)
+            except OSError as err:
+                if err.errno == errno.ENOSYS and pid_exists(self.pid):
+                    # I saw this happening on Travis:
+                    # https://travis-ci.org/giampaolo/psutil/jobs/51368273
+                    raise ZombieProcess(self.pid, self._name, self._ppid)
+                else:
+                    raise
 
     @wrap_exceptions
     def status(self):
-        f = open("/proc/%s/status" % self.pid, 'rb')
-        try:
-            STATE = b("State:")
+        with open("/proc/%s/status" % self.pid, 'rb') as f:
             for line in f:
-                if line.startswith(STATE):
+                if line.startswith(b"State:"):
                     letter = line.split()[1]
                     if PY3:
                         letter = letter.decode()
                     # XXX is '?' legit? (we're not supposed to return
                     # it anyway)
                     return PROC_STATUSES.get(letter, '?')
-        finally:
-            f.close()
 
     @wrap_exceptions
     def open_files(self):
         retlist = []
         files = os.listdir("/proc/%s/fd" % self.pid)
         hit_enoent = False
         for fd in files:
             file = "/proc/%s/fd/%s" % (self.pid, fd)
-            if os.path.islink(file):
-                try:
-                    file = os.readlink(file)
-                except OSError:
-                    # ENOENT == file which is gone in the meantime
-                    err = sys.exc_info()[1]
-                    if err.errno in (errno.ENOENT, errno.ESRCH):
-                        hit_enoent = True
-                        continue
+            try:
+                file = os.readlink(file)
+            except OSError as err:
+                # ENOENT == file which is gone in the meantime
+                if err.errno in (errno.ENOENT, errno.ESRCH):
+                    hit_enoent = True
+                    continue
+                elif err.errno == errno.EINVAL:
+                    # not a link
+                    continue
+                else:
                     raise
-                else:
-                    # If file is not an absolute path there's no way
-                    # to tell whether it's a regular file or not,
-                    # so we skip it. A regular file is always supposed
-                    # to be absolutized though.
-                    if file.startswith('/') and isfile_strict(file):
-                        ntuple = _common.popenfile(file, int(fd))
-                        retlist.append(ntuple)
+            else:
+                # If file is not an absolute path there's no way
+                # to tell whether it's a regular file or not,
+                # so we skip it. A regular file is always supposed
+                # to be absolutized though.
+                if file.startswith('/') and isfile_strict(file):
+                    ntuple = _common.popenfile(file, int(fd))
+                    retlist.append(ntuple)
         if hit_enoent:
             # raise NSP if the process disappeared on us
             os.stat('/proc/%s' % self.pid)
         return retlist
 
     @wrap_exceptions
     def connections(self, kind='inet'):
         ret = _connections.retrieve(kind, self.pid)
@@ -1183,44 +1172,35 @@ class Process(object):
         return ret
 
     @wrap_exceptions
     def num_fds(self):
         return len(os.listdir("/proc/%s/fd" % self.pid))
 
     @wrap_exceptions
     def ppid(self):
-        f = open("/proc/%s/status" % self.pid, 'rb')
-        try:
-            PPID = b("PPid:")
+        fpath = "/proc/%s/status" % self.pid
+        with open(fpath, 'rb') as f:
             for line in f:
-                if line.startswith(PPID):
+                if line.startswith(b"PPid:"):
                     # PPid: nnnn
                     return int(line.split()[1])
-            raise NotImplementedError("line not found")
-        finally:
-            f.close()
+            raise NotImplementedError("line 'PPid' not found in %s" % fpath)
 
     @wrap_exceptions
     def uids(self):
-        f = open("/proc/%s/status" % self.pid, 'rb')
-        try:
-            UID = b('Uid:')
+        fpath = "/proc/%s/status" % self.pid
+        with open(fpath, 'rb') as f:
             for line in f:
-                if line.startswith(UID):
+                if line.startswith(b'Uid:'):
                     _, real, effective, saved, fs = line.split()
                     return _common.puids(int(real), int(effective), int(saved))
-            raise NotImplementedError("line not found")
-        finally:
-            f.close()
+            raise NotImplementedError("line 'Uid' not found in %s" % fpath)
 
     @wrap_exceptions
     def gids(self):
-        f = open("/proc/%s/status" % self.pid, 'rb')
-        try:
-            GID = b('Gid:')
+        fpath = "/proc/%s/status" % self.pid
+        with open(fpath, 'rb') as f:
             for line in f:
-                if line.startswith(GID):
+                if line.startswith(b'Gid:'):
                     _, real, effective, saved, fs = line.split()
                     return _common.pgids(int(real), int(effective), int(saved))
-            raise NotImplementedError("line not found")
-        finally:
-            f.close()
+            raise NotImplementedError("line 'Gid' not found in %s" % fpath)
--- a/python/psutil/psutil/_psosx.py
+++ b/python/psutil/psutil/_psosx.py
@@ -2,32 +2,34 @@
 
 # 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.
 
 """OSX platform implementation."""
 
 import errno
+import functools
 import os
-import sys
+from collections import namedtuple
 
-from psutil import _common
-from psutil import _psposix
-from psutil._common import conn_tmap, usage_percent, isfile_strict
-from psutil._compat import namedtuple, wraps
-import _psutil_osx as cext
-import _psutil_posix
+from . import _common
+from . import _psposix
+from . import _psutil_osx as cext
+from . import _psutil_posix as cext_posix
+from ._common import conn_tmap, usage_percent, isfile_strict
+from ._common import sockfam_to_enum, socktype_to_enum
 
 
 __extra__all__ = []
 
 # --- constants
 
 PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+AF_LINK = cext_posix.AF_LINK
 
 # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
 TCP_STATUSES = {
     cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
     cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
     cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
     cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
     cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
@@ -60,16 +62,17 @@ pmmap_grouped = namedtuple(
     'pmmap_grouped',
     'path rss private swapped dirtied ref_count shadow_depth')
 
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
 
 # set later from __init__.py
 NoSuchProcess = None
+ZombieProcess = None
 AccessDenied = None
 TimeoutExpired = None
 
 
 # --- functions
 
 def virtual_memory():
     """System virtual memory as a namedtuple."""
@@ -160,52 +163,69 @@ def net_connections(kind='inet'):
         else:
             if cons:
                 for c in cons:
                     c = list(c) + [pid]
                     ret.append(_common.sconn(*c))
     return ret
 
 
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    names = net_io_counters().keys()
+    ret = {}
+    for name in names:
+        isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
+        if hasattr(_common, 'NicDuplex'):
+            duplex = _common.NicDuplex(duplex)
+        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+    return ret
+
+
 pids = cext.pids
 pid_exists = _psposix.pid_exists
 disk_usage = _psposix.disk_usage
 net_io_counters = cext.net_io_counters
 disk_io_counters = cext.disk_io_counters
+net_if_addrs = cext_posix.net_if_addrs
 
 
 def wrap_exceptions(fun):
     """Decorator which translates bare OSError exceptions into
     NoSuchProcess and AccessDenied.
     """
-    @wraps(fun)
+    @functools.wraps(fun)
     def wrapper(self, *args, **kwargs):
         try:
             return fun(self, *args, **kwargs)
-        except OSError:
+        except OSError as err:
             # support for private module import
-            if NoSuchProcess is None or AccessDenied is None:
+            if (NoSuchProcess is None or AccessDenied is None or
+                    ZombieProcess is None):
                 raise
-            err = sys.exc_info()[1]
             if err.errno == errno.ESRCH:
-                raise NoSuchProcess(self.pid, self._name)
+                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"]
+    __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
+        self._ppid = None
 
     @wrap_exceptions
     def name(self):
         return cext.proc_name(self.pid)
 
     @wrap_exceptions
     def exe(self):
         return cext.proc_exe(self.pid)
@@ -288,16 +308,18 @@ class Process(object):
             raise ValueError("invalid %r kind argument; choose between %s"
                              % (kind, ', '.join([repr(x) for x in conn_tmap])))
         families, types = conn_tmap[kind]
         rawlist = cext.proc_connections(self.pid, families, types)
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
             status = TCP_STATUSES[status]
+            fam = sockfam_to_enum(fam)
+            type = socktype_to_enum(type)
             nt = _common.pconn(fd, fam, type, laddr, raddr, status)
             ret.append(nt)
         return ret
 
     @wrap_exceptions
     def num_fds(self):
         if self.pid == 0:
             return 0
@@ -310,21 +332,21 @@ class Process(object):
         except _psposix.TimeoutExpired:
             # support for private module import
             if TimeoutExpired is None:
                 raise
             raise TimeoutExpired(timeout, self.pid, self._name)
 
     @wrap_exceptions
     def nice_get(self):
-        return _psutil_posix.getpriority(self.pid)
+        return cext_posix.getpriority(self.pid)
 
     @wrap_exceptions
     def nice_set(self, value):
-        return _psutil_posix.setpriority(self.pid, value)
+        return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def status(self):
         code = cext.proc_status(self.pid)
         # XXX is '?' legit? (we're not supposed to return it anyway)
         return PROC_STATUSES.get(code, '?')
 
     @wrap_exceptions
--- a/python/psutil/psutil/_psposix.py
+++ b/python/psutil/psutil/_psposix.py
@@ -7,18 +7,18 @@
 """Routines common to all posix systems."""
 
 import errno
 import glob
 import os
 import sys
 import time
 
-from psutil._common import sdiskusage, usage_percent, memoize
-from psutil._compat import PY3, unicode
+from ._common import sdiskusage, usage_percent, memoize
+from ._compat import PY3, unicode
 
 
 class TimeoutExpired(Exception):
     pass
 
 
 def pid_exists(pid):
     """Check whether pid exists in the current process table."""
@@ -26,18 +26,17 @@ def pid_exists(pid):
         # According to "man 2 kill" PID 0 has a special meaning:
         # it refers to <<every process in the process group of the
         # calling process>> so we don't want to go any further.
         # If we get here it means this UNIX platform *does* have
         # a process with id 0.
         return True
     try:
         os.kill(pid, 0)
-    except OSError:
-        err = sys.exc_info()[1]
+    except OSError as err:
         if err.errno == errno.ESRCH:
             # ESRCH == No such process
             return False
         elif err.errno == errno.EPERM:
             # EPERM clearly means there's a process to deny access to
             return True
         else:
             # According to "man 2 kill" possible error values are
@@ -64,38 +63,39 @@ def wait_pid(pid, timeout=None):
         if timeout is not None:
             if timer() >= stop_at:
                 raise TimeoutExpired()
         time.sleep(delay)
         return min(delay * 2, 0.04)
 
     timer = getattr(time, 'monotonic', time.time)
     if timeout is not None:
-        waitcall = lambda: os.waitpid(pid, os.WNOHANG)
+        def waitcall():
+            return os.waitpid(pid, os.WNOHANG)
         stop_at = timer() + timeout
     else:
-        waitcall = lambda: os.waitpid(pid, 0)
+        def waitcall():
+            return os.waitpid(pid, 0)
 
     delay = 0.0001
-    while 1:
+    while True:
         try:
             retpid, status = waitcall()
-        except OSError:
-            err = sys.exc_info()[1]
+        except OSError as err:
             if err.errno == errno.EINTR:
                 delay = check_timeout(delay)
                 continue
             elif err.errno == errno.ECHILD:
                 # This has two meanings:
                 # - pid is not a child of os.getpid() in which case
                 #   we keep polling until it's gone
                 # - pid never existed in the first place
                 # In both cases we'll eventually return None as we
                 # can't determine its exit status code.
-                while 1:
+                while True:
                     if pid_exists(pid):
                         delay = check_timeout(delay)
                     else:
                         return
             else:
                 raise
         else:
             if retpid == 0:
@@ -145,13 +145,12 @@ def disk_usage(path):
 @memoize
 def _get_terminal_map():
     ret = {}
     ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
     for name in ls:
         assert name not in ret
         try:
             ret[os.stat(name).st_rdev] = name
-        except OSError:
-            err = sys.exc_info()[1]
+        except OSError as err:
             if err.errno != errno.ENOENT:
                 raise
     return ret
--- a/python/psutil/psutil/_pssunos.py
+++ b/python/psutil/psutil/_pssunos.py
@@ -6,28 +6,31 @@
 
 """Sun OS Solaris platform implementation."""
 
 import errno
 import os
 import socket
 import subprocess
 import sys
+from collections import namedtuple
 
-from psutil import _common
-from psutil import _psposix
-from psutil._common import usage_percent, isfile_strict
-from psutil._compat import namedtuple, PY3
-import _psutil_posix
-import _psutil_sunos as cext
+from . import _common
+from . import _psposix
+from . import _psutil_posix as cext_posix
+from . import _psutil_sunos as cext
+from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum
+from ._common import usage_percent
+from ._compat import PY3
 
 
 __extra__all__ = ["CONN_IDLE", "CONN_BOUND"]
 
 PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = cext_posix.AF_LINK
 
 CONN_IDLE = "IDLE"
 CONN_BOUND = "BOUND"
 
 PROC_STATUSES = {
     cext.SSLEEP: _common.STATUS_SLEEPING,
     cext.SRUN: _common.STATUS_RUNNING,
     cext.SZOMB: _common.STATUS_ZOMBIE,
@@ -58,24 +61,26 @@ scputimes = namedtuple('scputimes', ['us
 svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
 pextmem = namedtuple('pextmem', ['rss', 'vms'])
 pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
 pmmap_ext = namedtuple(
     'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
 
 # set later from __init__.py
 NoSuchProcess = None
+ZombieProcess = None
 AccessDenied = None
 TimeoutExpired = None
 
 # --- functions
 
 disk_io_counters = cext.disk_io_counters
 net_io_counters = cext.net_io_counters
 disk_usage = _psposix.disk_usage
+net_if_addrs = cext_posix.net_if_addrs
 
 
 def virtual_memory():
     # we could have done this with kstat, but imho this is good enough
     total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
     # note: there's no difference on Solaris
     free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
     used = total - free
@@ -86,17 +91,19 @@ def virtual_memory():
 def swap_memory():
     sin, sout = cext.swap_mem()
     # XXX
     # we are supposed to get total/free by doing so:
     # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
     #     usr/src/cmd/swap/swap.c
     # ...nevertheless I can't manage to obtain the same numbers as 'swap'
     # cmdline utility, so let's parse its output (sigh!)
-    p = subprocess.Popen(['swap', '-l', '-k'], stdout=subprocess.PIPE)
+    p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
+                          os.environ['PATH'], 'swap', '-l', '-k'],
+                         stdout=subprocess.PIPE)
     stdout, stderr = p.communicate()
     if PY3:
         stdout = stdout.decode(sys.stdout.encoding)
     if p.returncode != 0:
         raise RuntimeError("'swap -l -k' failed (retcode=%s)" % p.returncode)
 
     lines = stdout.strip().split('\n')[1:]
     if not lines:
@@ -204,63 +211,80 @@ def net_connections(kind, _pid=-1):
     cmap = _common.conn_tmap.copy()
     if _pid == -1:
         cmap.pop('unix', 0)
     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, families, types)
-    ret = []
+    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]
+        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.append(nt)
+        ret.add(nt)
+    return list(ret)
+
+
+def net_if_stats():
+    """Get NIC stats (isup, duplex, speed, mtu)."""
+    ret = cext.net_if_stats()
+    for name, items in ret.items():
+        isup, duplex, speed, mtu = items
+        if hasattr(_common, 'NicDuplex'):
+            duplex = _common.NicDuplex(duplex)
+        ret[name] = _common.snicstats(isup, duplex, speed, mtu)
     return ret
 
 
 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:
+        except EnvironmentError as err:
             # support for private module import
-            if NoSuchProcess is None or AccessDenied is None:
+            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.
-            err = sys.exc_info()[1]
             if err.errno in (errno.ENOENT, errno.ESRCH):
-                raise NoSuchProcess(self.pid, self._name)
+                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"]
+    __slots__ = ["pid", "_name", "_ppid"]
 
     def __init__(self, pid):
         self.pid = pid
         self._name = None
+        self._ppid = None
 
     @wrap_exceptions
     def name(self):
         # note: max len == 15
         return cext.proc_name_and_args(self.pid)[0]
 
     @wrap_exceptions
     def exe(self):
@@ -287,33 +311,34 @@ class Process(object):
         # For some reason getpriority(3) return ESRCH (no such process)
         # for certain low-pid processes, no matter what (even as root).
         # The process actually exists though, as it has a name,
         # creation time, etc.
         # The best thing we can do here appears to be raising AD.
         # Note: tested on Solaris 11; on Open Solaris 5 everything is
         # fine.
         try:
-            return _psutil_posix.getpriority(self.pid)
-        except EnvironmentError:
-            err = sys.exc_info()[1]
-            if err.errno in (errno.ENOENT, errno.ESRCH):
+            return cext_posix.getpriority(self.pid)
+        except EnvironmentError as err:
+            # 48 is 'operation not supported' but errno does not expose
+            # it. It occurs for low system pids.
+            if err.errno in (errno.ENOENT, errno.ESRCH, 48):
                 if pid_exists(self.pid):
                     raise AccessDenied(self.pid, self._name)
             raise
 
     @wrap_exceptions
     def nice_set(self, value):
         if self.pid in (2, 3):
             # Special case PIDs: internally setpriority(3) return ESRCH
             # (no such process), no matter what.
             # The process actually exists though, as it has a name,
             # creation time, etc.
             raise AccessDenied(self.pid, self._name)
-        return _psutil_posix.setpriority(self.pid, value)
+        return cext_posix.setpriority(self.pid, value)
 
     @wrap_exceptions
     def ppid(self):
         return cext.proc_basic_info(self.pid)[0]
 
     @wrap_exceptions
     def uids(self):
         real, effective, saved, _, _, _ = cext.proc_cred(self.pid)
@@ -333,36 +358,34 @@ class Process(object):
     def terminal(self):
         hit_enoent = False
         tty = wrap_exceptions(
             cext.proc_basic_info(self.pid)[0])
         if tty != cext.PRNODEV:
             for x in (0, 1, 2, 255):
                 try:
                     return os.readlink('/proc/%d/path/%d' % (self.pid, x))
-                except OSError:
-                    err = sys.exc_info()[1]
+                except OSError as err:
                     if err.errno == errno.ENOENT:
                         hit_enoent = True
                         continue
                     raise
         if hit_enoent:
             # raise NSP if the process disappeared on us
             os.stat('/proc/%s' % self.pid)
 
     @wrap_exceptions
     def cwd(self):
         # /proc/PID/path/cwd may not be resolved by readlink() even if
         # it exists (ls shows it). If that's the case and the process
         # is still alive return None (we can return None also on BSD).
         # Reference: http://goo.gl/55XgO
         try:
             return os.readlink("/proc/%s/path/cwd" % self.pid)
-        except OSError:
-            err = sys.exc_info()[1]
+        except OSError as err:
             if err.errno == errno.ENOENT:
                 os.stat("/proc/%s" % self.pid)
                 return None
             raise
 
     @wrap_exceptions
     def memory_info(self):
         ret = cext.proc_basic_info(self.pid)
@@ -383,19 +406,18 @@ class Process(object):
         ret = []
         tids = os.listdir('/proc/%d/lwp' % self.pid)
         hit_enoent = False
         for tid in tids:
             tid = int(tid)
             try:
                 utime, stime = cext.query_process_thread(
                     self.pid, tid)
-            except EnvironmentError:
+            except EnvironmentError as err:
                 # ENOENT == thread gone in meantime
-                err = sys.exc_info()[1]
                 if err.errno == errno.ENOENT:
                     hit_enoent = True
                     continue
                 raise
             else:
                 nt = _common.pthread(tid, utime, stime)
                 ret.append(nt)
         if hit_enoent:
@@ -408,19 +430,18 @@ class Process(object):
         retlist = []
         hit_enoent = False
         pathdir = '/proc/%d/path' % self.pid
         for fd in os.listdir('/proc/%d/fd' % self.pid):
             path = os.path.join(pathdir, fd)
             if os.path.islink(path):
                 try:
                     file = os.readlink(path)
-                except OSError:
+                except OSError as err:
                     # ENOENT == file which is gone in the meantime
-                    err = sys.exc_info()[1]
                     if err.errno == errno.ENOENT:
                         hit_enoent = True
                         continue
                     raise
                 else:
                     if isfile_strict(file):
                         retlist.append(_common.popenfile(file, int(fd)))
         if hit_enoent:
@@ -490,18 +511,17 @@ class Process(object):
         rawlist = cext.proc_memory_maps(self.pid)
         hit_enoent = False
         for item in rawlist:
             addr, addrsize, perm, name, rss, anon, locked = item
             addr = toaddr(addr, addrsize)
             if not name.startswith('['):
                 try:
                     name = os.readlink('/proc/%s/path/%s' % (self.pid, name))
-                except OSError:
-                    err = sys.exc_info()[1]
+                except OSError as err:
                     if err.errno == errno.ENOENT:
                         # sometimes the link may not be resolved by
                         # readlink() even if it exists (ls shows it).
                         # If that's the case we just return the
                         # unresolved link path.
                         # This seems an incosistency with /proc similar
                         # to: http://goo.gl/55XgO
                         name = '/proc/%s/path/%s' % (self.pid, name)
--- a/python/psutil/psutil/_psutil_bsd.c
+++ b/python/psutil/psutil/_psutil_bsd.c
@@ -16,22 +16,24 @@
 #include <fcntl.h>
 #include <paths.h>
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <sys/param.h>
 #include <sys/user.h>
 #include <sys/proc.h>
 #include <sys/file.h>
+#include <sys/cpuset.h>
 #include <net/route.h>
 
 #include <sys/socket.h>
 #include <sys/socketvar.h>    // for struct xsocket
 #include <sys/un.h>
 #include <sys/unpcb.h>
+#include <sys/sockio.h>
 // for xinpcb struct
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/in_pcb.h>
 #include <netinet/tcp_var.h>   // for struct xtcpcb
 #include <netinet/tcp_fsm.h>   // for TCP connection states
 #include <arpa/inet.h>         // for inet_ntop()
@@ -44,16 +46,17 @@
 #include <devstat.h>      // get io counters
 #include <sys/vmmeter.h>  // needed for vmtotal struct
 #include <libutil.h>      // process open files, shared libs (kinfo_getvmmap)
 #include <sys/mount.h>
 
 #include <net/if.h>       // net io counters
 #include <net/if_dl.h>
 #include <net/route.h>
+#include <net/if_media.h>
 
 #include <netinet/in.h>   // process open files/connections
 #include <sys/un.h>
 
 #include "_psutil_bsd.h"
 #include "_psutil_common.h"
 #include "arch/bsd/process_info.h"
 
@@ -87,31 +90,42 @@ psutil_kinfo_proc(const pid_t pid, struc
         NoSuchProcess();
         return -1;
     }
     return 0;
 }
 
 
 /*
+ * Set exception to AccessDenied if pid exists else NoSuchProcess.
+ */
+void
+psutil_raise_ad_or_nsp(long pid) {
+    if (psutil_pid_exists(pid) == 0)
+        NoSuchProcess();
+    else
+        AccessDenied();
+}
+
+
+/*
  * Return a Python list of all the PIDs running on the system.
  */
 static PyObject *
 psutil_pids(PyObject *self, PyObject *args)
 {
     kinfo_proc *proclist = NULL;
     kinfo_proc *orig_address = NULL;
     size_t num_processes;
     size_t idx;
     PyObject *retlist = PyList_New(0);
     PyObject *pid = NULL;
 
-    if (retlist == NULL) {
+    if (retlist == NULL)
         return NULL;
-    }
     if (psutil_get_proc_list(&proclist, &num_processes) != 0) {
         PyErr_SetString(PyExc_RuntimeError,
                         "failed to retrieve process list.");
         goto error;
     }
 
     if (num_processes > 0) {
         orig_address = proclist; // save so we can free it after we're done
@@ -127,19 +141,18 @@ psutil_pids(PyObject *self, PyObject *ar
         free(orig_address);
     }
 
     return retlist;
 
 error:
     Py_XDECREF(pid);
     Py_DECREF(retlist);
-    if (orig_address != NULL) {
+    if (orig_address != NULL)
         free(orig_address);
-    }
     return NULL;
 }
 
 
 /*
  * Return a Python float indicating the system boot time expressed in
  * seconds since the epoch.
  */
@@ -162,22 +175,20 @@ psutil_boot_time(PyObject *self, PyObjec
 /*
  * Return process name from kinfo_proc as a Python string.
  */
 static PyObject *
 psutil_proc_name(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("s", kp.ki_comm);
 }
 
 
 /*
  * Return process pathname executable.
  * Thanks to Robert N. M. Watson:
  * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
@@ -186,201 +197,182 @@ static PyObject *
 psutil_proc_exe(PyObject *self, PyObject *args)
 {
     long pid;
     char pathname[PATH_MAX];
     int error;
     int mib[4];
     size_t size;
 
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
 
     mib[0] = CTL_KERN;
     mib[1] = KERN_PROC;
     mib[2] = KERN_PROC_PATHNAME;
     mib[3] = pid;
 
     size = sizeof(pathname);
     error = sysctl(mib, 4, pathname, &size, NULL, 0);
     if (error == -1) {
         PyErr_SetFromErrno(PyExc_OSError);
         return NULL;
     }
     if (size == 0 || strlen(pathname) == 0) {
-        if (psutil_pid_exists(pid) == 0) {
+        if (psutil_pid_exists(pid) == 0)
             return NoSuchProcess();
-        }
-        else {
+        else
             strcpy(pathname, "");
-        }
     }
     return Py_BuildValue("s", pathname);
 }
 
 
 /*
  * Return process cmdline as a Python list of cmdline arguments.
  */
 static PyObject *
 psutil_proc_cmdline(PyObject *self, PyObject *args)
 {
     long pid;
     PyObject *arglist = NULL;
 
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
 
     // get the commandline, defined in arch/bsd/process_info.c
     arglist = psutil_get_arg_list(pid);
 
     // psutil_get_arg_list() returns NULL only if psutil_cmd_args
     // failed with ESRCH (no process with that PID)
-    if (NULL == arglist) {
+    if (NULL == arglist)
         return PyErr_SetFromErrno(PyExc_OSError);
-    }
     return Py_BuildValue("N", arglist);
 }
 
 
 /*
  * Return process parent pid from kinfo_proc as a Python integer.
  */
 static PyObject *
 psutil_proc_ppid(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("l", (long)kp.ki_ppid);
 }
 
 
 /*
  * Return process status as a Python integer.
  */
 static PyObject *
 psutil_proc_status(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("i", (int)kp.ki_stat);
 }
 
 
 /*
  * Return process real, effective and saved user ids from kinfo_proc
  * as a Python tuple.
  */
 static PyObject *
 psutil_proc_uids(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("lll",
                          (long)kp.ki_ruid,
                          (long)kp.ki_uid,
                          (long)kp.ki_svuid);
 }
 
 
 /*
  * Return process real, effective and saved group ids from kinfo_proc
  * as a Python tuple.
  */
 static PyObject *
 psutil_proc_gids(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("lll",
                          (long)kp.ki_rgid,
                          (long)kp.ki_groups[0],
                          (long)kp.ki_svuid);
 }
 
 
 /*
  * Return process real, effective and saved group ids from kinfo_proc
  * as a Python tuple.
  */
 static PyObject *
 psutil_proc_tty_nr(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("i", kp.ki_tdev);
 }
 
 
 /*
  * Return the number of context switches performed by process as a tuple.
  */
 static PyObject *
 psutil_proc_num_ctx_switches(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("(ll)",
                          kp.ki_rusage.ru_nvcsw,
                          kp.ki_rusage.ru_nivcsw);
 }
 
 
 /*
  * Return number of threads used by process as a Python integer.
  */
 static PyObject *
 psutil_proc_num_threads(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("l", (long)kp.ki_numthreads);
 }
 
 
 /*
  * Retrieves all threads used by process returning a list of tuples
  * including thread id, user time and system time.
  * Thanks to Robert N. M. Watson:
@@ -451,38 +443,35 @@ psutil_proc_threads(PyObject *self, PyOb
         Py_DECREF(pyTuple);
     }
     free(kip);
     return retList;
 
 error:
     Py_XDECREF(pyTuple);
     Py_DECREF(retList);
-    if (kip != NULL) {
+    if (kip != NULL)
         free(kip);
-    }
     return NULL;
 }
 
 
 /*
  * Return a Python tuple (user_time, kernel_time)
  */
 static PyObject *
 psutil_proc_cpu_times(PyObject *self, PyObject *args)
 {
     long pid;
     double user_t, sys_t;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     // convert from microseconds to seconds
     user_t = TV2DOUBLE(kp.ki_rusage.ru_utime);
     sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime);
     return Py_BuildValue("(dd)", user_t, sys_t);
 }
 
 
 /*
@@ -495,91 +484,87 @@ psutil_cpu_count_logical(PyObject *self,
     int mib[2];
     int ncpu;
     size_t len;
 
     mib[0] = CTL_HW;
     mib[1] = HW_NCPU;
     len = sizeof(ncpu);
 
-    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
-        // mimic os.cpu_count()
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    else {
+    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
+        Py_RETURN_NONE;  // mimic os.cpu_count()
+    else
         return Py_BuildValue("i", ncpu);
-    }
 }
 
 
 /*
  * Return an XML string from which we'll determine the number of
  * physical CPU cores in the system.
  */
 static PyObject *
 psutil_cpu_count_phys(PyObject *self, PyObject *args)
 {
     void *topology = NULL;
     size_t size = 0;
+    PyObject *py_str;
 
     if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0))
         goto error;
 
     topology = malloc(size);
     if (!topology) {
         PyErr_NoMemory();
         return NULL;
     }
 
     if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0))
         goto error;
 
-    return Py_BuildValue("s", topology);
+    py_str = Py_BuildValue("s", topology);
+    free(topology);
+    return py_str;
 
 error:
-    Py_INCREF(Py_None);
-    return Py_None;
+    if (topology != NULL)
+        free(topology);
+    Py_RETURN_NONE;
 }
 
 
 /*
  * Return a Python float indicating the process create time expressed in
  * seconds since the epoch.
  */
 static PyObject *
 psutil_proc_create_time(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("d", TV2DOUBLE(kp.ki_start));
 }
 
 
 /*
  * Return a Python float indicating the process create time expressed in
  * seconds since the epoch.
  */
 static PyObject *
 psutil_proc_io_counters(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     // there's apparently no way to determine bytes count, hence return -1.
     return Py_BuildValue("(llll)",
                          kp.ki_rusage.ru_inblock,
                          kp.ki_rusage.ru_oublock,
                          -1,
                          -1);
 }
 
@@ -587,22 +572,20 @@ psutil_proc_io_counters(PyObject *self, 
 /*
  * Return extended memory info for a process as a Python tuple.
  */
 static PyObject *
 psutil_proc_memory_info(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("(lllll)",
                          ptoa(kp.ki_rssize),    // rss
                          (long)kp.ki_size,      // vms
                          ptoa(kp.ki_tsize),     // text
                          ptoa(kp.ki_dsize),     // data
                          ptoa(kp.ki_ssize));    // stack
 }
 
@@ -744,17 +727,20 @@ psutil_cpu_times(PyObject *self, PyObjec
  * These functions are available on FreeBSD 8 only.
  * In the upper python layer we do various tricks to avoid crashing
  * and/or to provide alternatives where possible.
  */
 
 
 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
 /*
- * Return files opened by process as a list of (path, fd) tuples
+ * Return files opened by process as a list of (path, fd) tuples.
+ * TODO: this is broken as it may report empty paths. 'procstat'
+ * utility has the same problem see:
+ * https://github.com/giampaolo/psutil/issues/595
  */
 static PyObject *
 psutil_proc_open_files(PyObject *self, PyObject *args)
 {
     long pid;
     int i, cnt;
     struct kinfo_file *freep = NULL;
     struct kinfo_file *kif;
@@ -862,19 +848,18 @@ psutil_proc_cwd(PyObject *self, PyObject
             break;
         }
     }
     /*
      * For lower pids it seems we can't retrieve any information
      * (lsof can't do that it either).  Since this happens even
      * as root we return an empty string instead of AccessDenied.
      */
-    if (path == NULL) {
+    if (path == NULL)
         path = Py_BuildValue("s", "");
-    }
     free(freep);
     return path;
 
 error:
     Py_XDECREF(path);
     if (freep != NULL)
         free(freep);
     return NULL;
@@ -882,17 +867,16 @@ error:
 
 
 // The tcplist fetching and walking is borrowed from netstat/inet.c.
 static char *
 psutil_fetch_tcplist(void)
 {
     char *buf;
     size_t len;
-    int error;
 
     for (;;) {
         if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) {
             PyErr_SetFromErrno(PyExc_OSError);
             return NULL;
         }
         buf = malloc(len);
         if (buf == NULL) {
@@ -912,32 +896,34 @@ static int
 psutil_sockaddr_port(int family, struct sockaddr_storage *ss)
 {
     struct sockaddr_in6 *sin6;
     struct sockaddr_in *sin;
 
     if (family == AF_INET) {
         sin = (struct sockaddr_in *)ss;
         return (sin->sin_port);
-    } else {
+    }
+    else {
         sin6 = (struct sockaddr_in6 *)ss;
         return (sin6->sin6_port);
     }
 }
 
 static void *
 psutil_sockaddr_addr(int family, struct sockaddr_storage *ss)
 {
     struct sockaddr_in6 *sin6;
     struct sockaddr_in *sin;
 
     if (family == AF_INET) {
         sin = (struct sockaddr_in *)ss;
         return (&sin->sin_addr);
-    } else {
+    }
+    else {
         sin6 = (struct sockaddr_in6 *)ss;
         return (&sin6->sin6_addr);
     }
 }
 
 static socklen_t
 psutil_sockaddr_addrlen(int family)
 {
@@ -1025,22 +1011,20 @@ psutil_proc_connections(PyObject *self, 
     PyObject *tuple = NULL;
     PyObject *laddr = NULL;
     PyObject *raddr = NULL;
     PyObject *af_filter = NULL;
     PyObject *type_filter = NULL;
     PyObject *_family = NULL;
     PyObject *_type = NULL;
 
-    if (retList == NULL) {
+    if (retList == NULL)
         return NULL;
-    }
-    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) {
+    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter))
         goto error;
-    }
     if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
         PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
         goto error;
     }
 
     freep = kinfo_getfile(pid, &cnt);
     if (freep == NULL) {
         psutil_raise_ad_or_nsp(pid);
@@ -1058,32 +1042,28 @@ psutil_proc_connections(PyObject *self, 
         char lip[200], rip[200];
         char path[PATH_MAX];
         int inseq;
         tuple = NULL;
         laddr = NULL;
         raddr = NULL;
 
         kif = &freep[i];
-        if (kif->kf_type == KF_TYPE_SOCKET)
-        {
+        if (kif->kf_type == KF_TYPE_SOCKET) {
             // apply filters
             _family = PyLong_FromLong((long)kif->kf_sock_domain);
             inseq = PySequence_Contains(af_filter, _family);
             Py_DECREF(_family);
-            if (inseq == 0) {
+            if (inseq == 0)
                 continue;
-            }
             _type = PyLong_FromLong((long)kif->kf_sock_type);
             inseq = PySequence_Contains(type_filter, _type);
             Py_DECREF(_type);
-            if (inseq == 0) {
+            if (inseq == 0)
                 continue;
-            }
-
             // IPv4 / IPv6 socket
             if ((kif->kf_sock_domain == AF_INET) ||
                     (kif->kf_sock_domain == AF_INET6)) {
                 // fill status
                 state = PSUTIL_CONN_NONE;
                 if (kif->kf_sock_type == SOCK_STREAM) {
                     tcp = psutil_search_tcplist(tcplist, kif);
                     if (tcp != NULL)
@@ -1107,22 +1087,20 @@ psutil_proc_connections(PyObject *self, 
                                                    &kif->kf_sa_local));
                 rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
                                                    &kif->kf_sa_peer));
 
                 // construct python tuple/list
                 laddr = Py_BuildValue("(si)", lip, lport);
                 if (!laddr)
                     goto error;
-                if (rport != 0) {
+                if (rport != 0)
                     raddr = Py_BuildValue("(si)", rip, rport);
-                }
-                else {
+                else
                     raddr = Py_BuildValue("()");
-                }
                 if (!raddr)
                     goto error;
                 tuple = Py_BuildValue("(iiiNNi)",
                                       kif->kf_fd,
                                       kif->kf_sock_domain,
                                       kif->kf_sock_type,
                                       laddr,
                                       raddr,
@@ -1135,17 +1113,17 @@ psutil_proc_connections(PyObject *self, 
             }
             // UNIX socket
             else if (kif->kf_sock_domain == AF_UNIX) {
                 struct sockaddr_un *sun;
 
                 sun = (struct sockaddr_un *)&kif->kf_sa_local;
                 snprintf(
                     path, sizeof(path), "%.*s",
-                    (sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+                    (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
                     sun->sun_path);
 
                 tuple = Py_BuildValue("(iiisOi)",
                                       kif->kf_fd,
                                       kif->kf_sock_domain,
                                       kif->kf_sock_type,
                                       path,
                                       Py_None,
@@ -1245,49 +1223,46 @@ error:
 
 // remove spaces from string
 void remove_spaces(char *str) {
     char *p1 = str;
     char *p2 = str;
     do
         while (*p2 == ' ')
             p2++;
-    while (*p1++ = *p2++);
+    while ((*p1++ = *p2++));
 }
 
 
 /*
  * Return a list of tuples for every process memory maps.
  * 'procstat' cmdline utility has been used as an example.
  */
 static PyObject *
 psutil_proc_memory_maps(PyObject *self, PyObject *args)
 {
     long pid;
     int ptrwidth;
     int i, cnt;
-    char addr[30];
+    char addr[1000];
     char perms[4];
     const char *path;
     struct kinfo_proc kp;
     struct kinfo_vmentry *freep = NULL;
     struct kinfo_vmentry *kve;
     ptrwidth = 2 * sizeof(void *);
     PyObject *pytuple = NULL;
     PyObject *retlist = PyList_New(0);
 
-    if (retlist == NULL) {
+    if (retlist == NULL)
         return NULL;
-    }
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         goto error;
-    }
-    if (psutil_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_kinfo_proc(pid, &kp) == -1)
         goto error;
-    }
 
     freep = kinfo_getvmmap(pid, &cnt);
     if (freep == NULL) {
         psutil_raise_ad_or_nsp(pid);
         goto error;
     }
     for (i = 0; i < cnt; i++) {
         pytuple = NULL;
@@ -1301,46 +1276,46 @@ psutil_proc_memory_maps(PyObject *self, 
                 sizeof(perms));
         strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
                 sizeof(perms));
         strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
                 sizeof(perms));
 
         if (strlen(kve->kve_path) == 0) {
             switch (kve->kve_type) {
-            case KVME_TYPE_NONE:
-                path = "[none]";
-                break;
-            case KVME_TYPE_DEFAULT:
-                path = "[default]";
-                break;
-            case KVME_TYPE_VNODE:
-                path = "[vnode]";
-                break;
-            case KVME_TYPE_SWAP:
-                path = "[swap]";
-                break;
-            case KVME_TYPE_DEVICE:
-                path = "[device]";
-                break;
-            case KVME_TYPE_PHYS:
-                path = "[phys]";
-                break;
-            case KVME_TYPE_DEAD:
-                path = "[dead]";
-                break;
-            case KVME_TYPE_SG:
-                path = "[sg]";
-                break;
-            case KVME_TYPE_UNKNOWN:
-                path = "[unknown]";
-                break;
-            default:
-                path = "[?]";
-                break;
+                case KVME_TYPE_NONE:
+                    path = "[none]";
+                    break;
+                case KVME_TYPE_DEFAULT:
+                    path = "[default]";
+                    break;
+                case KVME_TYPE_VNODE:
+                    path = "[vnode]";
+                    break;
+                case KVME_TYPE_SWAP:
+                    path = "[swap]";
+                    break;
+                case KVME_TYPE_DEVICE:
+                    path = "[device]";
+                    break;
+                case KVME_TYPE_PHYS:
+                    path = "[phys]";
+                    break;
+                case KVME_TYPE_DEAD:
+                    path = "[dead]";
+                    break;
+                case KVME_TYPE_SG:
+                    path = "[sg]";
+                    break;
+                case KVME_TYPE_UNKNOWN:
+                    path = "[unknown]";
+                    break;
+                default:
+                    path = "[?]";
+                    break;
             }
         }
         else {
             path = kve->kve_path;
         }
 
         pytuple = Py_BuildValue("sssiiii",
             addr,                       // "start-end" address
@@ -1484,19 +1459,19 @@ static PyObject *
 psutil_net_io_counters(PyObject *self, PyObject *args)
 {
     char *buf = NULL, *lim, *next;
     struct if_msghdr *ifm;
     int mib[6];
     size_t len;
     PyObject *py_retdict = PyDict_New();
     PyObject *py_ifc_info = NULL;
+
     if (py_retdict == NULL)
         return NULL;
-
     mib[0] = CTL_NET;          // networking subsystem
     mib[1] = PF_ROUTE;         // type of information
     mib[2] = 0;                // protocol (IPPROTO_xxx)
     mib[3] = 0;                // address family
     mib[4] = NET_RT_IFLIST;   // operation
     mib[5] = 0;
 
     if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
@@ -1528,19 +1503,18 @@ psutil_net_io_counters(PyObject *self, P
             char ifc_name[32];
 
             strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
             ifc_name[sdl->sdl_nlen] = 0;
             // XXX: ignore usbus interfaces:
             // http://lists.freebsd.org/pipermail/freebsd-current/
             //     2011-October/028752.html
             // 'ifconfig -a' doesn't show them, nor do we.
-            if (strncmp(ifc_name, "usbus", 5) == 0) {
+            if (strncmp(ifc_name, "usbus", 5) == 0)
                 continue;
-            }
 
             py_ifc_info = Py_BuildValue("(kkkkkkki)",
                                         if2m->ifm_data.ifi_obytes,
                                         if2m->ifm_data.ifi_ibytes,
                                         if2m->ifm_data.ifi_opackets,
                                         if2m->ifm_data.ifi_ipackets,
                                         if2m->ifm_data.ifi_ierrors,
                                         if2m->ifm_data.ifi_oerrors,
@@ -1575,19 +1549,19 @@ error:
 static PyObject *
 psutil_disk_io_counters(PyObject *self, PyObject *args)
 {
     int i;
     struct statinfo stats;
 
     PyObject *py_retdict = PyDict_New();
     PyObject *py_disk_info = NULL;
+
     if (py_retdict == NULL)
         return NULL;
-
     if (devstat_checkversion(NULL) < 0) {
         PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed");
         goto error;
     }
 
     stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
     if (stats.dinfo == NULL) {
         PyErr_NoMemory();
@@ -1621,19 +1595,18 @@ psutil_disk_io_counters(PyObject *self, 
                 &current.duration[DEVSTAT_WRITE], NULL));  // w time
         if (!py_disk_info)
             goto error;
         if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
             goto error;
         Py_DECREF(py_disk_info);
     }
 
-    if (stats.dinfo->mem_ptr) {
+    if (stats.dinfo->mem_ptr)
         free(stats.dinfo->mem_ptr);
-    }
     free(stats.dinfo);
     return py_retdict;
 
 error:
     Py_XDECREF(py_disk_info);
     Py_DECREF(py_retdict);
     if (stats.dinfo != NULL)
         free(stats.dinfo);
@@ -1761,62 +1734,63 @@ int
 psutil_get_pid_from_sock(int sock_hash)
 {
     struct xfile *xf;
     int hash, n;
     for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {
         if (xf->xf_data == NULL)
             continue;
         hash = (int)((uintptr_t)xf->xf_data % HASHSIZE);
-        if (sock_hash == hash) {
+        if (sock_hash == hash)
             return xf->xf_pid;
-        }
     }
     return -1;
 }
 
 
+// Reference:
+// https://gitorious.org/freebsd/freebsd/source/
+//     f1d6f4778d2044502209708bc167c05f9aa48615:usr.bin/sockstat/sockstat.c
 int psutil_gather_inet(int proto, PyObject *py_retlist)
 {
     struct xinpgen *xig, *exig;
     struct xinpcb *xip;
     struct xtcpcb *xtp;
     struct inpcb *inp;
     struct xsocket *so;
-    struct sock *sock;
-    const char *varname;
+    const char *varname = NULL;
     size_t len, bufsize;
     void *buf;
-    int hash, retry, vflag, type;
+    int hash;
+    int retry;
+    int type;
 
     PyObject *tuple = NULL;
     PyObject *laddr = NULL;
     PyObject *raddr = NULL;
 
     switch (proto) {
-    case IPPROTO_TCP:
-        varname = "net.inet.tcp.pcblist";
-        type = SOCK_STREAM;
-        break;
-    case IPPROTO_UDP:
-        varname = "net.inet.udp.pcblist";
-        type = SOCK_DGRAM;
-        break;
+        case IPPROTO_TCP:
+            varname = "net.inet.tcp.pcblist";
+            type = SOCK_STREAM;
+            break;
+        case IPPROTO_UDP:
+            varname = "net.inet.udp.pcblist";
+            type = SOCK_DGRAM;
+            break;
     }
 
     buf = NULL;
     bufsize = 8192;
     retry = 5;
     do {
         for (;;) {
             buf = realloc(buf, bufsize);
-            if (buf == NULL) {
-                // XXX
-                continue;
-            }
+            if (buf == NULL)
+                continue;  // XXX
             len = bufsize;
             if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
                 break;
             if (errno != ENOMEM) {
                 PyErr_SetFromErrno(0);
                 goto error;
             }
             bufsize *= 2;
@@ -1826,83 +1800,88 @@ int psutil_gather_inet(int proto, PyObje
         if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {
             PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
             goto error;
         }
     } while (xig->xig_gen != exig->xig_gen && retry--);
 
 
     for (;;) {
+        int lport, rport, pid, status, family;
+
         xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
         if (xig >= exig)
             break;
 
         switch (proto) {
-        case IPPROTO_TCP:
-            xtp = (struct xtcpcb *)xig;
-            if (xtp->xt_len != sizeof *xtp) {
-                PyErr_Format(PyExc_RuntimeError, "struct xtcpcb size mismatch");
+            case IPPROTO_TCP:
+                xtp = (struct xtcpcb *)xig;
+                if (xtp->xt_len != sizeof *xtp) {
+                    PyErr_Format(PyExc_RuntimeError,
+                                 "struct xtcpcb size mismatch");
+                    goto error;
+                }
+                inp = &xtp->xt_inp;
+                so = &xtp->xt_socket;
+                status = xtp->xt_tp.t_state;
+                break;
+            case IPPROTO_UDP:
+                xip = (struct xinpcb *)xig;
+                if (xip->xi_len != sizeof *xip) {
+                    PyErr_Format(PyExc_RuntimeError,
+                                 "struct xinpcb size mismatch");
+                    goto error;
+                }
+                inp = &xip->xi_inp;
+                so = &xip->xi_socket;
+                status = PSUTIL_CONN_NONE;
+                break;
+            default:
+                PyErr_Format(PyExc_RuntimeError, "invalid proto");
                 goto error;
-            }
-            break;
-        case IPPROTO_UDP:
-            xip = (struct xinpcb *)xig;
-            if (xip->xi_len != sizeof *xip) {
-                PyErr_Format(PyExc_RuntimeError, "struct xinpcb size mismatch");
-                goto error;
-            }
-            inp = &xip->xi_inp;
-            so = &xip->xi_socket;
-            break;
         }
 
-        inp = &xtp->xt_inp;
-        so = &xtp->xt_socket;
         char lip[200], rip[200];
-        int family, lport, rport, pid, status;
 
         hash = (int)((uintptr_t)so->xso_so % HASHSIZE);
         pid = psutil_get_pid_from_sock(hash);
         if (pid < 0)
             continue;
         lport = ntohs(inp->inp_lport);
         rport = ntohs(inp->inp_fport);
-        status = xtp->xt_tp.t_state;
 
         if (inp->inp_vflag & INP_IPV4) {
             family = AF_INET;
             inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));
             inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));
         }
         else if (inp->inp_vflag & INP_IPV6) {
             family = AF_INET6;
             inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));
             inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));
         }
 
         // construct python tuple/list
         laddr = Py_BuildValue("(si)", lip, lport);
         if (!laddr)
             goto error;
-        if (rport != 0) {
+        if (rport != 0)
             raddr = Py_BuildValue("(si)", rip, rport);
-        }
-        else {
+        else
             raddr = Py_BuildValue("()");
-        }
         if (!raddr)
             goto error;
         tuple = Py_BuildValue("(iiiNNii)", -1, family, type, laddr, raddr,
                                                status, pid);
         if (!tuple)
             goto error;
         if (PyList_Append(py_retlist, tuple))
             goto error;
         Py_DECREF(tuple);
-  }
+    }
 
     free(buf);
     return 1;
 
 error:
     Py_XDECREF(tuple);
     Py_XDECREF(laddr);
     Py_XDECREF(raddr);
@@ -1910,38 +1889,40 @@ error:
     return 0;
 }
 
 
 int psutil_gather_unix(int proto, PyObject *py_retlist)
 {
     struct xunpgen *xug, *exug;
     struct xunpcb *xup;
-    struct sock *sock;
-    const char *varname, *protoname;
-    size_t len, bufsize;
+    const char *varname = NULL;
+    const char *protoname = NULL;
+    size_t len;
+    size_t bufsize;
     void *buf;
-    int hash, retry;
-    int family, lport, rport, pid;
+    int hash;
+    int retry;
+    int pid;
     struct sockaddr_un *sun;
     char path[PATH_MAX];
 
     PyObject *tuple = NULL;
     PyObject *laddr = NULL;
     PyObject *raddr = NULL;
 
     switch (proto) {
-    case SOCK_STREAM:
-        varname = "net.local.stream.pcblist";
-        protoname = "stream";
-        break;
-    case SOCK_DGRAM:
-        varname = "net.local.dgram.pcblist";
-        protoname = "dgram";
-        break;
+        case SOCK_STREAM:
+            varname = "net.local.stream.pcblist";
+            protoname = "stream";
+            break;
+        case SOCK_DGRAM:
+            varname = "net.local.dgram.pcblist";
+            protoname = "dgram";
+            break;
     }
 
     buf = NULL;
     bufsize = 8192;
     retry = 5;
 
     do {
         for (;;) {
@@ -1968,29 +1949,27 @@ int psutil_gather_unix(int proto, PyObje
         }
     } while (xug->xug_gen != exug->xug_gen && retry--);
 
     for (;;) {
         xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
         if (xug >= exug)
             break;
         xup = (struct xunpcb *)xug;
-        if (xup->xu_len != sizeof *xup) {
-            warnx("struct xunpgen size mismatch");
+        if (xup->xu_len != sizeof *xup)
             goto error;
-        }
 
         hash = (int)((uintptr_t) xup->xu_socket.xso_so % HASHSIZE);
         pid = psutil_get_pid_from_sock(hash);
         if (pid < 0)
             continue;
 
         sun = (struct sockaddr_un *)&xup->xu_addr;
         snprintf(path, sizeof(path), "%.*s",
-                 (sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+                 (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
                  sun->sun_path);
 
         tuple = Py_BuildValue("(iiisOii)", -1, AF_UNIX, proto, path, Py_None,
                                                PSUTIL_CONN_NONE, pid);
         if (!tuple)
             goto error;
         if (PyList_Append(py_retlist, tuple))
             goto error;
@@ -2011,23 +1990,22 @@ error:
 
 
 /*
  * Return system-wide open connections.
  */
 static PyObject*
 psutil_net_connections(PyObject* self, PyObject* args)
 {
-    PyObject *af_filter = NULL;
-    PyObject *type_filter = NULL;
     PyObject *py_retlist = PyList_New(0);
 
+    if (py_retlist == NULL)
+        return NULL;
     if (psutil_populate_xfiles() != 1)
         goto error;
-
     if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0)
         goto error;
     if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0)
         goto error;
     if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0)
        goto error;
     if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0)
         goto error;
@@ -2038,16 +2016,117 @@ psutil_net_connections(PyObject* self, P
 error:
     Py_DECREF(py_retlist);
     free(psutil_xfiles);
     return NULL;
 }
 
 
 /*
+ * Get process CPU affinity.
+ * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
+ */
+static PyObject*
+psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args)
+{
+    long pid;
+    int ret;
+    int i;
+    cpuset_t mask;
+    PyObject* py_retlist;
+    PyObject* py_cpu_num;
+
+    if (!PyArg_ParseTuple(args, "i", &pid))
+        return NULL;
+    ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+                             sizeof(mask), &mask);
+    if (ret != 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL)
+        return NULL;
+
+    for (i = 0; i < CPU_SETSIZE; i++) {
+        if (CPU_ISSET(i, &mask)) {
+            py_cpu_num = Py_BuildValue("i", i);
+            if (py_cpu_num == NULL)
+                goto error;
+            if (PyList_Append(py_retlist, py_cpu_num))
+                goto error;
+        }
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cpu_num);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+/*
+ * Set process CPU affinity.
+ * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
+ */
+static PyObject *
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args)
+{
+    long pid;
+    int i;
+    int seq_len;
+    int ret;
+    cpuset_t cpu_set;
+    PyObject *py_cpu_set;
+    PyObject *py_cpu_seq = NULL;
+
+    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
+        return NULL;
+
+    py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
+    if (!py_cpu_seq)
+        return NULL;
+    seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
+
+    // calculate the mask
+    CPU_ZERO(&cpu_set);
+    for (i = 0; i < seq_len; i++) {
+        PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
+#if PY_MAJOR_VERSION >= 3
+        long value = PyLong_AsLong(item);
+#else
+        long value = PyInt_AsLong(item);
+#endif
+        if (value == -1 && PyErr_Occurred())
+            goto error;
+        CPU_SET(value, &cpu_set);
+    }
+
+    // set affinity
+    ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+                             sizeof(cpu_set), &cpu_set);
+    if (ret != 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    Py_DECREF(py_cpu_seq);
+    Py_RETURN_NONE;
+
+error:
+    if (py_cpu_seq != NULL)
+        Py_DECREF(py_cpu_seq);
+    return NULL;
+}
+
+
+/*
  * define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
 PsutilMethods[] =
 {
     // --- per-process functions
 
     {"proc_name", psutil_proc_name, METH_VARARGS,
@@ -2078,16 +2157,20 @@ PsutilMethods[] =
     {"proc_threads", psutil_proc_threads, METH_VARARGS,
      "Return process threads"},
     {"proc_status", psutil_proc_status, METH_VARARGS,
      "Return process status as an integer"},
     {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
      "Return process IO counters"},
     {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS,
      "Return process tty (terminal) number"},
+    {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
+     "Return process CPU affinity."},
+    {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
+     "Set process CPU affinity."},
 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
     {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
      "Return files opened by process as a list of (path, fd) tuples"},
     {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
      "Return process current working directory."},
     {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
      "Return a list of tuples for every process's memory map"},
     {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
@@ -2176,16 +2259,18 @@ PyMODINIT_FUNC PyInit__psutil_bsd(void)
 void init_psutil_bsd(void)
 #endif
 {
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
     PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods);
 #endif
+    PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+
     // process status constants
     PyModule_AddIntConstant(module, "SSTOP", SSTOP);
     PyModule_AddIntConstant(module, "SSLEEP", SSLEEP);
     PyModule_AddIntConstant(module, "SRUN", SRUN);
     PyModule_AddIntConstant(module, "SIDL", SIDL);
     PyModule_AddIntConstant(module, "SWAIT", SWAIT);
     PyModule_AddIntConstant(module, "SLOCK", SLOCK);
     PyModule_AddIntConstant(module, "SZOMB", SZOMB);
@@ -2198,15 +2283,14 @@ void init_psutil_bsd(void)
     PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
     PyModule_AddIntConstant(module, "TCPS_SYN_RECEIVED", 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);
 
-    if (module == NULL) {
+    if (module == NULL)
         INITERROR;
-    }
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
--- a/python/psutil/psutil/_psutil_bsd.h
+++ b/python/psutil/psutil/_psutil_bsd.h
@@ -21,16 +21,18 @@ static PyObject* psutil_proc_name(PyObje
 static PyObject* psutil_proc_num_ctx_switches(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_ppid(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_status(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_tty_nr(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_uids(PyObject* self, PyObject* args);
+static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
+static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
 
 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
 static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
 #endif
 
 // --- system-related functions
 
@@ -43,9 +45,9 @@ static PyObject* psutil_disk_partitions(
 static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
 static PyObject* psutil_pids(PyObject* self, PyObject* args);
 static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
 static PyObject* psutil_users(PyObject* self, PyObject* args);
 static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
 
 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
 static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
-#endif
\ No newline at end of file
+#endif
--- a/python/psutil/psutil/_psutil_linux.c
+++ b/python/psutil/psutil/_psutil_linux.c
@@ -14,16 +14,21 @@
 #include <stdlib.h>
 #include <mntent.h>
 #include <features.h>
 #include <utmp.h>
 #include <sched.h>
 #include <linux/version.h>
 #include <sys/syscall.h>
 #include <sys/sysinfo.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/ethtool.h>
 
 #include "_psutil_linux.h"
 
 /* The minimum number of CPUs allocated in a cpu_set_t */
 static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT;
 
 // Linux >= 2.6.13
 #define PSUTIL_HAVE_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
@@ -41,16 +46,22 @@ static const int NCPUS_START = sizeof(un
 #endif
 
 
 #if PSUTIL_HAVE_IOPRIO
 enum {
     IOPRIO_WHO_PROCESS = 1,
 };
 
+// May happen on old RedHat versions, see:
+// https://github.com/giampaolo/psutil/issues/607
+#ifndef DUPLEX_UNKNOWN
+    #define DUPLEX_UNKNOWN 0xff
+#endif
+
 static inline int
 ioprio_get(int which, int who)
 {
     return syscall(__NR_ioprio_get, which, who);
 }
 
 static inline int
 ioprio_set(int which, int who, int ioprio)
@@ -69,23 +80,21 @@ ioprio_set(int which, int who, int iopri
 /*
  * Return a (ioclass, iodata) Python tuple representing process I/O priority.
  */
 static PyObject *
 psutil_proc_ioprio_get(PyObject *self, PyObject *args)
 {
     long pid;
     int ioprio, ioclass, iodata;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
     ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
-    if (ioprio == -1) {
+    if (ioprio == -1)
         return PyErr_SetFromErrno(PyExc_OSError);
-    }
     ioclass = IOPRIO_PRIO_CLASS(ioprio);
     iodata = IOPRIO_PRIO_DATA(ioprio);
     return Py_BuildValue("ii", ioclass, iodata);
 }
 
 
 /*
  * A wrapper around ioprio_set(); sets process I/O priority.
@@ -94,26 +103,23 @@ psutil_proc_ioprio_get(PyObject *self, P
  */
 static PyObject *
 psutil_proc_ioprio_set(PyObject *self, PyObject *args)
 {
     long pid;
     int ioprio, ioclass, iodata;
     int retval;
 
-    if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) {
+    if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata))
         return NULL;
-    }
     ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
     retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
-    if (retval == -1) {
+    if (retval == -1)
         return PyErr_SetFromErrno(PyExc_OSError);
-    }
-    Py_INCREF(Py_None);
-    return Py_None;
+    Py_RETURN_NONE;
 }
 #endif
 
 
 #if PSUTIL_HAVE_PRLIMIT
 /*
  * A wrapper around prlimit(2); sets process resource limits.
  * This can be used for both get and set, in which case extra
@@ -124,19 +130,18 @@ psutil_linux_prlimit(PyObject *self, PyO
 {
     long pid;
     int ret, resource;
     struct rlimit old, new;
     struct rlimit *newp = NULL;
     PyObject *soft = NULL;
     PyObject *hard = NULL;
 
-    if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &soft, &hard)) {
+    if (! PyArg_ParseTuple(args, "li|OO", &pid, &resource, &soft, &hard))
         return NULL;
-    }
 
     // get
     if (soft == NULL && hard == NULL) {
         ret = prlimit(pid, resource, NULL, &old);
         if (ret == -1)
             return PyErr_SetFromErrno(PyExc_OSError);
 #if defined(PSUTIL_HAVE_LONG_LONG)
         if (sizeof(old.rlim_cur) > sizeof(long)) {
@@ -164,18 +169,17 @@ psutil_linux_prlimit(PyObject *self, PyO
         new.rlim_max = PyLong_AsLong(hard);
         if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
             return NULL;
 #endif
         newp = &new;
         ret = prlimit(pid, resource, newp, &old);
         if (ret == -1)
             return PyErr_SetFromErrno(PyExc_OSError);
-        Py_INCREF(Py_None);
-        return Py_None;
+        Py_RETURN_NONE;
     }
 }
 #endif
 
 
 /*
  * Return disk mounted partitions as a list of tuples including device,
  * mount point and filesystem type
@@ -230,20 +234,19 @@ error:
 
 /*
  * A wrapper around sysinfo(), return system memory usage statistics.
  */
 static PyObject *
 psutil_linux_sysinfo(PyObject *self, PyObject *args)
 {
     struct sysinfo info;
-    if (sysinfo(&info) != 0) {
+
+    if (sysinfo(&info) != 0)
         return PyErr_SetFromErrno(PyExc_OSError);
-    }
-
     // note: boot time might also be determined from here
     return Py_BuildValue(
         "(KKKKKK)",
         (unsigned long long)info.totalram  * info.mem_unit,   // total
         (unsigned long long)info.freeram   * info.mem_unit,   // free
         (unsigned long long)info.bufferram * info.mem_unit,   // buffer
         (unsigned long long)info.sharedram * info.mem_unit,   // shared
         (unsigned long long)info.totalswap * info.mem_unit,   // swap tot
@@ -263,20 +266,18 @@ static PyObject *
 psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args)
 {
     int cpu, ncpus, count, cpucount_s;
     long pid;
     size_t setsize;
     cpu_set_t *mask = NULL;
     PyObject *res = NULL;
 
-    if (!PyArg_ParseTuple(args, "i", &pid)) {
+    if (!PyArg_ParseTuple(args, "i", &pid))
         return NULL;
-    }
-
     ncpus = NCPUS_START;
     while (1) {
         setsize = CPU_ALLOC_SIZE(ncpus);
         mask = CPU_ALLOC(ncpus);
         if (mask == NULL)
             return PyErr_NoMemory();
         if (sched_getaffinity(pid, setsize, mask) == 0)
             break;
@@ -298,127 +299,131 @@ psutil_proc_cpu_affinity_get(PyObject *s
     cpucount_s = CPU_COUNT_S(setsize, mask);
     for (cpu = 0, count = cpucount_s; count; cpu++) {
         if (CPU_ISSET_S(cpu, setsize, mask)) {
 #if PY_MAJOR_VERSION >= 3
             PyObject *cpu_num = PyLong_FromLong(cpu);
 #else
             PyObject *cpu_num = PyInt_FromLong(cpu);
 #endif
-            --count;
             if (cpu_num == NULL)
                 goto error;
             if (PyList_Append(res, cpu_num)) {
                 Py_DECREF(cpu_num);
                 goto error;
             }
             Py_DECREF(cpu_num);
+            --count;
         }
     }
     CPU_FREE(mask);
     return res;
 
 error:
     if (mask)
         CPU_FREE(mask);
     Py_XDECREF(res);
     return NULL;
 }
 #else
 
 
+/*
+ * Alternative implementation in case CPU_ALLOC is not defined.
+ */
 static PyObject *
 psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args)
 {
     cpu_set_t cpuset;
     unsigned int len = sizeof(cpu_set_t);
     long pid;
-	int i;
-	PyObject* ret_list;
-
-    if (!PyArg_ParseTuple(args, "i", &pid)) {
-        return NULL;
-    }
+    int i;
+    PyObject* py_retlist = NULL;
+    PyObject *py_cpu_num = NULL;
 
+    if (!PyArg_ParseTuple(args, "i", &pid))
+        return NULL;
 	CPU_ZERO(&cpuset);
-    if (sched_getaffinity(pid, len, &cpuset) < 0) {
+    if (sched_getaffinity(pid, len, &cpuset) < 0)
         return PyErr_SetFromErrno(PyExc_OSError);
-    }
 
-    ret_list = PyList_New(0);
-
+    py_retlist = PyList_New(0);
+    if (py_retlist == NULL)
+        goto error;
     for (i = 0; i < CPU_SETSIZE; ++i) {
         if (CPU_ISSET(i, &cpuset)) {
-            PyList_Append(ret_list, Py_BuildValue("i", i));
+            py_cpu_num = Py_BuildValue("i", i);
+            if (py_cpu_num == NULL)
+                goto error;
+            if (PyList_Append(py_retlist, py_cpu_num))
+                goto error;
+            Py_DECREF(py_cpu_num);
         }
     }
 
-    return ret_list;
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cpu_num);
+    Py_DECREF(py_retlist);
+    return NULL;
 }
 #endif
 
 /*
  * Set process CPU affinity; expects a bitmask
  */
 static PyObject *
 psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args)
 {
     cpu_set_t cpu_set;
     size_t len;
     long pid;
     int i, seq_len;
     PyObject *py_cpu_set;
     PyObject *py_cpu_seq = NULL;
 
-    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set)) {
-        goto error;
-    }
+    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
+        return NULL;
 
     if (!PySequence_Check(py_cpu_set)) {
-        // does not work on Python 2.4
-        // PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s",
-        //              Py_TYPE(py_cpu_set)->tp_name);
-        PyErr_Format(PyExc_TypeError, "sequence argument expected");
+        PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s",
+                     Py_TYPE(py_cpu_set)->tp_name);
         goto error;
     }
 
     py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
-    if (!py_cpu_seq) {
+    if (!py_cpu_seq)
         goto error;
-    }
     seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
     CPU_ZERO(&cpu_set);
     for (i = 0; i < seq_len; i++) {
         PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
 #if PY_MAJOR_VERSION >= 3
         long value = PyLong_AsLong(item);
 #else
         long value = PyInt_AsLong(item);
 #endif
-        if (value == -1 && PyErr_Occurred()) {
+        if (value == -1 && PyErr_Occurred())
             goto error;
-        }
         CPU_SET(value, &cpu_set);
     }
 
     len = sizeof(cpu_set);
     if (sched_setaffinity(pid, len, &cpu_set)) {
         PyErr_SetFromErrno(PyExc_OSError);
         goto error;
     }
 
     Py_DECREF(py_cpu_seq);
-    Py_INCREF(Py_None);
-    return Py_None;
+    Py_RETURN_NONE;
 
 error:
-    if (py_cpu_seq != NULL) {
+    if (py_cpu_seq != NULL)
         Py_DECREF(py_cpu_seq);
-	}
-
     return NULL;
 }
 
 
 /*
  * Return currently connected users as a list of tuples.
  */
 static PyObject *
@@ -461,16 +466,97 @@ error:
     Py_XDECREF(user_proc);
     Py_DECREF(ret_list);
     endutent();
     return NULL;
 }
 
 
 /*
+ * Return stats about a particular network
+ * interface.  References:
+ * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject*
+psutil_net_if_stats(PyObject* self, PyObject* args)
+{
+    char *nic_name;
+    int sock = 0;
+    int ret;
+    int duplex;
+    int speed;
+    int mtu;
+    struct ifreq ifr;
+    struct ethtool_cmd ethcmd;
+    PyObject *py_is_up = NULL;
+    PyObject *py_ret = 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;
+
+    // duplex and speed
+    memset(&ethcmd, 0, sizeof ethcmd);
+    ethcmd.cmd = ETHTOOL_GSET;
+    ifr.ifr_data = (caddr_t)&ethcmd;
+    ret = ioctl(sock, SIOCETHTOOL, &ifr);
+
+    if (ret != -1) {
+        duplex = ethcmd.duplex;
+        speed = ethcmd.speed;
+    }
+    else {
+        if (errno == EOPNOTSUPP) {
+            // we typically get here in case of wi-fi cards
+            duplex = DUPLEX_UNKNOWN;
+            speed = 0;
+        }
+        else {
+            goto error;
+        }
+    }
+
+    close(sock);
+    py_ret = Py_BuildValue("[Oiii]", py_is_up, duplex, speed, mtu);
+    if (!py_ret)
+        goto error;
+    Py_DECREF(py_is_up);
+    return py_ret;
+
+error:
+    Py_XDECREF(py_is_up);
+    if (sock != 0)
+        close(sock);
+    PyErr_SetFromErrno(PyExc_OSError);
+    return NULL;
+}
+
+
+/*
  * Define the psutil C module methods and initialize the module.
  */
 static PyMethodDef
 PsutilMethods[] =
 {
     // --- per-process functions
 
 #if PSUTIL_HAVE_IOPRIO
@@ -486,16 +572,18 @@ PsutilMethods[] =
 
     // --- system related functions
 
     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
      "Return disk mounted partitions as a list of tuples including "
      "device, mount point and filesystem type"},
     {"users", psutil_users, METH_VARARGS,
      "Return currently connected users as a list of tuples"},
+    {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+     "Return NIC stats (isup, duplex, speed, mtu)"},
 
     // --- linux specific
 
     {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS,
      "A wrapper around sysinfo(), return system memory usage statistics"},
 #if PSUTIL_HAVE_PRLIMIT
     {"linux_prlimit", psutil_linux_prlimit, METH_VARARGS,
      "Get or set process resource limits."},
@@ -554,16 +642,17 @@ void init_psutil_linux(void)
 {
 #if PY_MAJOR_VERSION >= 3
     PyObject *module = PyModule_Create(&moduledef);
 #else
     PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods);
 #endif
 
 
+    PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
 #if PSUTIL_HAVE_PRLIMIT
     PyModule_AddIntConstant(module, "RLIM_INFINITY", RLIM_INFINITY);
     PyModule_AddIntConstant(module, "RLIMIT_AS", RLIMIT_AS);
     PyModule_AddIntConstant(module, "RLIMIT_CORE", RLIMIT_CORE);
     PyModule_AddIntConstant(module, "RLIMIT_CPU", RLIMIT_CPU);
     PyModule_AddIntConstant(module, "RLIMIT_DATA", RLIMIT_DATA);
     PyModule_AddIntConstant(module, "RLIMIT_FSIZE", RLIMIT_FSIZE);
     PyModule_AddIntConstant(module, "RLIMIT_LOCKS", RLIMIT_LOCKS);
@@ -583,16 +672,18 @@ void init_psutil_linux(void)
 #endif
 #ifdef RLIMIT_RTTIME
     PyModule_AddIntConstant(module, "RLIMIT_RTTIME", RLIMIT_RTTIME);
 #endif
 #ifdef RLIMIT_SIGPENDING
     PyModule_AddIntConstant(module, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING);
 #endif
 #endif
+    PyModule_AddIntConstant(module, "DUPLEX_HALF", DUPLEX_HALF);
+    PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL);
+    PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN);
 
-    if (module == NULL) {
+    if (module == NULL)
         INITERROR;
-    }
 #if PY_MAJOR_VERSION >= 3
     return module;
 #endif
 }
--- a/python/psutil/psutil/_psutil_linux.h
+++ b/python/psutil/psutil/_psutil_linux.h
@@ -13,8 +13,9 @@ static PyObject* psutil_proc_cpu_affinit
 static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args);
 static PyObject* psutil_proc_ioprio_get(PyObject* self, PyObject* args);
 
 // system
 
 static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
 static PyObject* psutil_linux_sysinfo(PyObject* self, PyObject* args);
 static PyObject* psutil_users(PyObject* self, PyObject* args);
+static PyObject* psutil_net_if_stats(PyObject* self, PyObject* args);
--- a/python/psutil/psutil/_psutil_osx.c
+++ b/python/psutil/psutil/_psutil_osx.c
@@ -115,38 +115,35 @@ error:
 /*
  * Return process name from kinfo_proc as a Python string.
  */
 static PyObject *
 psutil_proc_name(PyObject *self, PyObject *args)
 {
     long pid;
     struct kinfo_proc kp;
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
-    if (psutil_get_kinfo_proc(pid, &kp) == -1) {
+    if (psutil_get_kinfo_proc(pid, &kp) == -1)
         return NULL;
-    }
     return Py_BuildValue("s", kp.kp_proc.p_comm);
 }
 
 
 /*
  * Return process current working directory.
  */
 static PyObject *
 psutil_proc_cwd(PyObject *self, PyObject *args)
 {
     long pid;
     struct proc_vnodepathinfo pathinfo;
 
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
 
     if (! psutil_proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, &pathinfo,
                               sizeof(pathinfo)))
     {
         return NULL;
     }
     return Py_BuildValue("s", pathinfo.pvi_cdir.vip_path);
 }
@@ -157,125 +154,113 @@ psutil_proc_cwd(PyObject *self, PyObject
  */
 static PyObject *
 psutil_proc_exe(PyObject *self, PyObject *args)
 {
     long pid;
     char buf[PATH_MAX];
     int ret;
 
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return NULL;
-    }
     ret = proc_pidpath(pid, &buf, sizeof(buf));
     if (ret == 0) {
-        if (! psutil_pid_exists(pid)) {
+        if (! psutil_pid_exists(pid))
             return NoSuchProcess();
-        }
-        else {
+        else
             return AccessDenied();
-        }
     }
     return Py_BuildValue("s", buf);
 }
 
 
 /*
  * Return process cmdline as a Python list of cmdline arguments.
  */
 static PyObject *
 psutil_proc_cmdline(PyObject *self, PyObject *args)
 {
     long pid;
     PyObject *arglist = NULL;
 
-    if (! PyArg_ParseTuple(args, "l", &pid)) {
+    if (! PyArg_ParseTuple(args, "l", &pid))
         return