bug 1481612 - Re-vendor psutil 5.4.3 using `mach vendor python`. r=gps
☠☠ backed out by 641b4a378923 ☠ ☠
authorTed Mielczarek <ted@mielczarek.org>
Wed, 15 Aug 2018 10:39:35 -0400
changeset 496241 c8f12706c4218440a04cbcb822f032306360d0cb
parent 496240 77d431411954e637de7c035485d391cd775225f4
child 496242 1ecd5ddcdd796533dc7d4f057280ae04da660a5a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1481612
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1481612 - Re-vendor psutil 5.4.3 using `mach vendor python`. r=gps psutil was previously vendored manually, so re-vendor it using our new tooling to make for smaller diffs in follow-up patches. This mostly just winds up adding some extra files that must have been left out of the manual vendoring, but it also updates the root Pipfile + Pipfile.lock. Differential Revision: https://phabricator.services.mozilla.com/D3434
Pipfile
Pipfile.lock
third_party/python/certifi/PKG-INFO
third_party/python/certifi/certifi/__init__.py
third_party/python/certifi/certifi/cacert.pem
third_party/python/certifi/setup.py
third_party/python/psutil/.coveragerc
third_party/python/psutil/.git-pre-commit
third_party/python/psutil/.gitignore
third_party/python/psutil/psutil/_psutil_aix.c
third_party/python/psutil/psutil/tests/__main__.py
third_party/python/psutil/psutil/tests/test_aix.py
third_party/python/psutil/psutil/tests/test_bsd.py
third_party/python/psutil/psutil/tests/test_connections.py
third_party/python/psutil/psutil/tests/test_contracts.py
third_party/python/psutil/psutil/tests/test_linux.py
third_party/python/psutil/psutil/tests/test_memory_leaks.py
third_party/python/psutil/psutil/tests/test_misc.py
third_party/python/psutil/psutil/tests/test_osx.py
third_party/python/psutil/psutil/tests/test_posix.py
third_party/python/psutil/psutil/tests/test_process.py
third_party/python/psutil/psutil/tests/test_sunos.py
third_party/python/psutil/psutil/tests/test_system.py
third_party/python/psutil/psutil/tests/test_unicode.py
third_party/python/psutil/psutil/tests/test_windows.py
third_party/python/psutil/scripts/battery.py
third_party/python/psutil/scripts/cpu_distribution.py
third_party/python/psutil/scripts/disk_usage.py
third_party/python/psutil/scripts/fans.py
third_party/python/psutil/scripts/free.py
third_party/python/psutil/scripts/ifconfig.py
third_party/python/psutil/scripts/internal/bench_oneshot.py
third_party/python/psutil/scripts/internal/check_broken_links.py
third_party/python/psutil/scripts/internal/download_exes.py
third_party/python/psutil/scripts/internal/generate_manifest.py
third_party/python/psutil/scripts/internal/print_announce.py
third_party/python/psutil/scripts/internal/winmake.py
third_party/python/psutil/scripts/iotop.py
third_party/python/psutil/scripts/killall.py
third_party/python/psutil/scripts/meminfo.py
third_party/python/psutil/scripts/netstat.py
third_party/python/psutil/scripts/nettop.py
third_party/python/psutil/scripts/pidof.py
third_party/python/psutil/scripts/pmap.py
third_party/python/psutil/scripts/procinfo.py
third_party/python/psutil/scripts/procsmem.py
third_party/python/psutil/scripts/ps.py
third_party/python/psutil/scripts/pstree.py
third_party/python/psutil/scripts/sensors.py
third_party/python/psutil/scripts/temperatures.py
third_party/python/psutil/scripts/top.py
third_party/python/psutil/scripts/who.py
third_party/python/psutil/scripts/winservices.py
--- a/Pipfile
+++ b/Pipfile
@@ -13,8 +13,9 @@ json-e = "==2.7.0"
 pip-tools = "==3.0.0"
 pipenv = "==2018.5.18"
 pytest = "==3.6.2"
 python-hglib = "==2.4"
 requests = "==2.9.1"
 six = "==1.10.0"
 virtualenv = "==15.2.0"
 voluptuous = "==0.11.5"
+psutil = "==5.4.3"
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -35,20 +35,20 @@
                 "sha256:b1fdd7e7a675295630f9ae71527a8ebc10bfefa236b3d6aa4932ee4462c17ba3",
                 "sha256:caad5211e7ba5afe04367cdd4cfc68fa886e2e08f6f35e76b7387d2109ccea6e"
             ],
             "index": "pypi",
             "version": "==1.7"
         },
         "certifi": {
             "hashes": [
-                "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
-                "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
+                "sha256:4c1d68a1408dd090d2f3a869aa94c3947cc1d967821d1ed303208c9f41f0f2f4",
+                "sha256:b6e8b28b2b7e771a41ecdd12d4d43262ecab52adebbafa42c77d6b57fb6ad3a4"
             ],
-            "version": "==2018.4.16"
+            "version": "==2018.8.13"
         },
         "click": {
             "hashes": [
                 "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
                 "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
             ],
             "version": "==7.0"
         },
@@ -64,20 +64,20 @@
             "hashes": [
                 "sha256:5d07bf0251a4128e5e8e8eef603849b6b5741c337bff087731a248f9cc774f56"
             ],
             "index": "pypi",
             "version": "==2.1.0"
         },
         "json-e": {
             "hashes": [
-                "sha256:d8c1ec3f5bbc7728c3a504ebe58829f283c64eca230871e4eefe974b4cdaae4a"
+                "sha256:f9114a25ed4b575395fbb2daa1183c5b781a647b387fdf28596220bb114673e8"
             ],
             "index": "pypi",
-            "version": "==2.7.0"
+            "version": "==2.5.0"
         },
         "more-itertools": {
             "hashes": [
                 "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
                 "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
                 "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
             ],
             "version": "==4.3.0"
@@ -101,16 +101,31 @@
         "pluggy": {
             "hashes": [
                 "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff",
                 "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c",
                 "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5"
             ],
             "version": "==0.6.0"
         },
+        "psutil": {
+            "hashes": [
+                "sha256:230eeb3aeb077814f3a2cd036ddb6e0f571960d327298cc914c02385c3e02a63",
+                "sha256:4152ae231709e3e8b80e26b6da20dc965a1a589959c48af1ed024eca6473f60d",
+                "sha256:779ec7e7621758ca11a8d99a1064996454b3570154277cc21342a01148a49c28",
+                "sha256:82a06785db8eeb637b349006cc28a92e40cd190fefae9875246d18d0de7ccac8",
+                "sha256:8a15d773203a1277e57b1d11a7ccdf70804744ef4a9518a87ab8436995c31a4b",
+                "sha256:94d4e63189f2593960e73acaaf96be235dd8a455fe2bcb37d8ad6f0e87f61556",
+                "sha256:a3286556d4d2f341108db65d8e20d0cd3fcb9a91741cb5eb496832d7daf2a97c",
+                "sha256:c91eee73eea00df5e62c741b380b7e5b6fdd553891bee5669817a3a38d036f13",
+                "sha256:e2467e9312c2fa191687b89ff4bc2ad8843be4af6fb4dc95a7cc5f7d7a327b18"
+            ],
+            "index": "pypi",
+            "version": "==5.4.3"
+        },
         "py": {
             "hashes": [
                 "sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
                 "sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
             ],
             "version": "==1.5.4"
         },
         "pytest": {
--- a/third_party/python/certifi/PKG-INFO
+++ b/third_party/python/certifi/PKG-INFO
@@ -1,11 +1,11 @@
 Metadata-Version: 1.1
 Name: certifi
-Version: 2018.4.16
+Version: 2018.8.13
 Summary: Python package for providing Mozilla's CA Bundle.
 Home-page: http://certifi.io/
 Author: Kenneth Reitz
 Author-email: me@kennethreitz.com
 License: MPL-2.0
 Description: Certifi: Python SSL Certificates
         ================================
         
--- a/third_party/python/certifi/certifi/__init__.py
+++ b/third_party/python/certifi/certifi/__init__.py
@@ -1,3 +1,3 @@
 from .core import where, old_where
 
-__version__ = "2018.04.16"
+__version__ = "2018.08.13"
--- a/third_party/python/certifi/certifi/cacert.pem
+++ b/third_party/python/certifi/certifi/cacert.pem
@@ -3687,179 +3687,16 @@ dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqG
 QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa
 jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC
 MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi
 C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep
 lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof
 TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
 -----END CERTIFICATE-----
 
-# Issuer: CN=Certplus Root CA G1 O=Certplus
-# Subject: CN=Certplus Root CA G1 O=Certplus
-# Label: "Certplus Root CA G1"
-# Serial: 1491911565779898356709731176965615564637713
-# MD5 Fingerprint: 7f:09:9c:f7:d9:b9:5c:69:69:56:d5:37:3e:14:0d:42
-# SHA1 Fingerprint: 22:fd:d0:b7:fd:a2:4e:0d:ac:49:2c:a0:ac:a6:7b:6a:1f:e3:f7:66
-# SHA256 Fingerprint: 15:2a:40:2b:fc:df:2c:d5:48:05:4d:22:75:b3:9c:7f:ca:3e:c0:97:80:78:b0:f0:ea:76:e5:61:a6:c7:43:3e
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA
-MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
-dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa
-MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
-dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a
-iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt
-6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP
-0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f
-6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE
-EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN
-1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc
-h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT
-mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV
-4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO
-WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud
-DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd
-Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq
-hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
-66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7
-/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS
-S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j
-2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R
-Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr
-RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy
-6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV
-V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5
-g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl
-++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
------END CERTIFICATE-----
-
-# Issuer: CN=Certplus Root CA G2 O=Certplus
-# Subject: CN=Certplus Root CA G2 O=Certplus
-# Label: "Certplus Root CA G2"
-# Serial: 1492087096131536844209563509228951875861589
-# MD5 Fingerprint: a7:ee:c4:78:2d:1b:ee:2d:b9:29:ce:d6:a7:96:32:31
-# SHA1 Fingerprint: 4f:65:8e:1f:e9:06:d8:28:02:e9:54:47:41:c9:54:25:5d:69:cc:1a
-# SHA256 Fingerprint: 6c:c0:50:41:e6:44:5e:74:69:6c:4c:fb:c9:f8:0f:54:3b:7e:ab:bb:44:b4:ce:6f:78:7c:6a:99:71:c4:2f:17
------BEGIN CERTIFICATE-----
-MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x
-CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
-dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x
-CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
-dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat
-93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x
-Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P
-AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj
-FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG
-SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch
-p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal
-U5ORGpOucGpnutee5WEaXw==
------END CERTIFICATE-----
-
-# Issuer: CN=OpenTrust Root CA G1 O=OpenTrust
-# Subject: CN=OpenTrust Root CA G1 O=OpenTrust
-# Label: "OpenTrust Root CA G1"
-# Serial: 1492036577811947013770400127034825178844775
-# MD5 Fingerprint: 76:00:cc:81:29:cd:55:5e:88:6a:7a:2e:f7:4d:39:da
-# SHA1 Fingerprint: 79:91:e8:34:f7:e2:ee:dd:08:95:01:52:e9:55:2d:14:e9:58:d5:7e
-# SHA256 Fingerprint: 56:c7:71:28:d9:8c:18:d9:1b:4c:fd:ff:bc:25:ee:91:03:d4:75:8e:a2:ab:ad:82:6a:90:f3:45:7d:46:0e:b4
------BEGIN CERTIFICATE-----
-MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA
-MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
-ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw
-MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
-T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b
-wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX
-/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0
-77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP
-uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx
-p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx
-Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2
-TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W
-G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw
-vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY
-EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1
-2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw
-DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
-PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf
-gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS
-FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0
-V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P
-XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I
-i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t
-TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91
-09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky
-Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ
-AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj
-1oxx
------END CERTIFICATE-----
-
-# Issuer: CN=OpenTrust Root CA G2 O=OpenTrust
-# Subject: CN=OpenTrust Root CA G2 O=OpenTrust
-# Label: "OpenTrust Root CA G2"
-# Serial: 1492012448042702096986875987676935573415441
-# MD5 Fingerprint: 57:24:b6:59:24:6b:ae:c8:fe:1c:0c:20:f2:c0:4e:eb
-# SHA1 Fingerprint: 79:5f:88:60:c5:ab:7c:3d:92:e6:cb:f4:8d:e1:45:cd:11:ef:60:0b
-# SHA256 Fingerprint: 27:99:58:29:fe:6a:75:15:c1:bf:e8:48:f9:c4:76:1d:b1:6c:22:59:29:25:7b:f4:0d:08:94:f2:9e:a8:ba:f2
------BEGIN CERTIFICATE-----
-MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA
-MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
-ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw
-MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
-T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh
-/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e
-CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6
-1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE
-FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS
-gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X
-G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy
-YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH
-vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4
-t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/
-gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3
-5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w
-DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
-Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0
-nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT
-RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT
-wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2
-t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa
-TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2
-o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU
-3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA
-iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f
-WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM
-S1IK
------END CERTIFICATE-----
-
-# Issuer: CN=OpenTrust Root CA G3 O=OpenTrust
-# Subject: CN=OpenTrust Root CA G3 O=OpenTrust
-# Label: "OpenTrust Root CA G3"
-# Serial: 1492104908271485653071219941864171170455615
-# MD5 Fingerprint: 21:37:b4:17:16:92:7b:67:46:70:a9:96:d7:a8:13:24
-# SHA1 Fingerprint: 6e:26:64:f3:56:bf:34:55:bf:d1:93:3f:7c:01:de:d8:13:da:8a:a6
-# SHA256 Fingerprint: b7:c3:62:31:70:6e:81:07:8c:36:7c:b8:96:19:8f:1e:32:08:dd:92:69:49:dd:8f:57:09:a4:10:f7:5b:62:92
------BEGIN CERTIFICATE-----
-MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx
-CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U
-cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow
-QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl
-blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm
-3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d
-oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G
-A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5
-DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK
-BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q
-j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx
-4nxp5V2a+EEfOzmTk51V6s2N8fvB
------END CERTIFICATE-----
-
 # Issuer: CN=ISRG Root X1 O=Internet Security Research Group
 # Subject: CN=ISRG Root X1 O=Internet Security Research Group
 # Label: "ISRG Root X1"
 # Serial: 172886928669790476064670243504169061120
 # MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e
 # SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8
 # SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6
 -----BEGIN CERTIFICATE-----
@@ -4393,8 +4230,48 @@ bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9y
 AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA
 VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku
 WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP
 MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX
 5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ
 ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg
 h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
 -----END CERTIFICATE-----
+
+# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
+# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
+# Label: "GlobalSign Root CA - R6"
+# Serial: 1417766617973444989252670301619537
+# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae
+# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1
+# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
+MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
+bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
+MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET
+MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI
+xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k
+ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD
+aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw
+LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw
+1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX
+k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2
+SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h
+bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n
+WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY
+rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce
+MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu
+bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
+nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt
+Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61
+55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj
+vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf
+cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz
+oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp
+nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs
+pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v
+JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
+8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4
+5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
+-----END CERTIFICATE-----
--- a/third_party/python/certifi/setup.py
+++ b/third_party/python/certifi/setup.py
@@ -44,24 +44,24 @@ setup(
         'certifi',
     ],
     package_dir={'certifi': 'certifi'},
     package_data={'certifi': ['*.pem']},
     # data_files=[('certifi', ['certifi/cacert.pem'])],
     include_package_data=True,
     zip_safe=False,
     license='MPL-2.0',
-    classifiers=(
+    classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
         'Natural Language :: English',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
-    ),
+    ],
 )
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/.coveragerc
@@ -0,0 +1,32 @@
+[report]
+
+include =
+    *psutil*
+omit =
+    psutil/_compat.py
+    psutil/tests/*
+    setup.py
+exclude_lines =
+    enum.IntEnum
+    except ImportError:
+    globals().update
+    if __name__ == .__main__.:
+    if _WINDOWS:
+    if BSD
+    if enum is None:
+    if enum is not None:
+    if FREEBSD
+    if has_enums:
+    if LINUX
+    if LITTLE_ENDIAN:
+    if NETBSD
+    if OPENBSD
+    if OSX
+    if ppid_map is None:
+    if PY3:
+    if SUNOS
+    if sys.platform.startswith
+    if WINDOWS
+    import enum
+    pragma: no cover
+    raise NotImplementedError
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/.git-pre-commit
@@ -0,0 +1,118 @@
+#!/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.
+
+"""
+This gets executed on 'git commit' and rejects the commit in case the
+submitted code does not pass validation. Validation is run only against
+the *.py files which were modified in the commit. Checks:
+
+- assert no space at EOLs
+- assert not pdb.set_trace in code
+- assert no bare except clause ("except:") in code
+- assert "flake8" returns no warnings
+
+Install this with "make install-git-hooks".
+"""
+
+from __future__ import print_function
+import os
+import subprocess
+import sys
+
+
+def term_supports_colors():
+    try:
+        import curses
+        assert sys.stderr.isatty()
+        curses.setupterm()
+        assert curses.tigetnum("colors") > 0
+    except Exception:
+        return False
+    else:
+        return True
+
+
+def hilite(s, ok=True, bold=False):
+    """Return an highlighted version of 'string'."""
+    if not term_supports_colors():
+        return s
+    attr = []
+    if ok is None:  # no color
+        pass
+    elif ok:   # green
+        attr.append('32')
+    else:   # red
+        attr.append('31')
+    if bold:
+        attr.append('1')
+    return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
+
+
+def exit(msg):
+    msg = hilite(msg, ok=False)
+    print(msg, file=sys.stderr)
+    sys.exit(1)
+
+
+def sh(cmd):
+    """run cmd in a subprocess and return its output.
+    raises RuntimeError on error.
+    """
+    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE, universal_newlines=True)
+    stdout, stderr = p.communicate()
+    if p.returncode != 0:
+        raise RuntimeError(stderr)
+    if stderr:
+        print(stderr, file=sys.stderr)
+    if stdout.endswith('\n'):
+        stdout = stdout[:-1]
+    return stdout
+
+
+def main():
+    out = sh("git diff --cached --name-only")
+    py_files = [x for x in out.split('\n') if x.endswith('.py') and
+                os.path.exists(x)]
+
+    lineno = 0
+    for path in py_files:
+        with open(path) as f:
+            for line in f:
+                lineno += 1
+                # space at end of line
+                if line.endswith(' '):
+                    print("%s:%s %r" % (path, lineno, line))
+                    return exit(
+                        "commit aborted: space at end of line")
+                line = line.rstrip()
+                # pdb
+                if "pdb.set_trace" in line:
+                    print("%s:%s %s" % (path, lineno, line))
+                    return exit(
+                        "commit aborted: you forgot a pdb in your python code")
+                # bare except clause
+                if "except:" in line and not line.endswith("# NOQA"):
+                    print("%s:%s %s" % (path, lineno, line))
+                    return exit("commit aborted: bare except clause")
+
+    # flake8
+    if py_files:
+        try:
+            import flake8  # NOQA
+        except ImportError:
+            return exit("commit aborted: flake8 is not installed; "
+                        "run 'make setup-dev-env'")
+
+        # XXX: we should scape spaces and possibly other amenities here
+        ret = subprocess.call(
+            "%s -m flake8 %s" % (sys.executable, " ".join(py_files)),
+            shell=True)
+        if ret != 0:
+            return exit("commit aborted: python code is not flake8 compliant")
+
+
+main()
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/.gitignore
@@ -0,0 +1,18 @@
+syntax: glob
+*.al
+*.bak
+*.egg-info
+*.la
+*.lo
+*.o
+*.orig
+*.pyc
+*.pyd
+*.rej
+*.so
+*.swp
+.cache/
+.idea/
+.tox/
+build/
+dist/
new file mode 100644
--- /dev/null
+++ b/third_party/python/psutil/psutil/_psutil_aix.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * AIX support is experimental at this time.
+ * The following functions and methods are unsupported on the AIX platform:
+ * - psutil.Process.memory_maps
+ *
+ * Known limitations:
+ * - psutil.Process.io_counters read count is always 0
+ * - psutil.Process.threads may not be available on older AIX versions
+ * - reading basic process info may fail or return incorrect values when
+ *   process is starting (see IBM APAR IV58499 - fixed in newer AIX versions)
+ * - sockets and pipes may not be counted in num_fds (fixed in newer AIX
+ *    versions)
+ *
+ * Useful resources:
+ * - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/
+ *       ssw_aix_61/com.ibm.aix.files/proc.htm
+ * - libperfstat: http://www-01.ibm.com/support/knowledgecenter/
+ *       ssw_aix_61/com.ibm.aix.files/libperfstat.h.htm
+ */
+
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/sysinfo.h>
+#include <sys/procfs.h>
+#include <sys/socket.h>
+#include <sys/thread.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/tihdr.h>
+#include <stropts.h>
+#include <netinet/tcp_fsm.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <libperfstat.h>
+
+#include "arch/aix/ifaddrs.h"
+#include "arch/aix/net_connections.h"
+#include "arch/aix/common.h"
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
+
+
+#define TV2DOUBLE(t)   (((t).tv_nsec * 0.000000001) + (t).tv_sec)
+
+/*
+ * Read a file content and fills a C structure with it.
+ */
+int
+psutil_file_to_struct(char *path, void *fstruct, size_t size) {
+    int fd;
+    size_t nbytes;
+    fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+        return 0;
+    }
+    nbytes = read(fd, fstruct, size);
+    if (nbytes <= 0) {
+        close(fd);
+        PyErr_SetFromErrno(PyExc_OSError);
+        return 0;
+    }
+    if (nbytes != size) {
+        close(fd);
+        PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
+        return 0;
+    }
+    close(fd);
+    return nbytes;
+}
+
+
+/*
+ * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_basic_info(PyObject *self, PyObject *args) {
+    int pid;
+    char path[100];
+    psinfo_t info;
+    pstatus_t status;
+    const char *procfs_path;
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+
+    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+        return NULL;
+
+    if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {
+        // From the /proc docs: "If the process is a zombie, the pr_nlwp
+        // and pr_lwp.pr_lwpid flags are zero."
+        status.pr_stat = SZOMB;
+    } else if (info.pr_flag & SEXIT) {
+        // "exiting" processes don't have /proc/<pid>/status
+        // There are other "exiting" processes that 'ps' shows as "active"
+        status.pr_stat = SACTIVE;
+    } else {
+        sprintf(path, "%s/%i/status", procfs_path, pid);
+        if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
+            return NULL;
+    }
+
+    return Py_BuildValue("KKKdiiiK",
+        (unsigned long long) info.pr_ppid,      // parent pid
+        (unsigned long long) info.pr_rssize,    // rss
+        (unsigned long long) info.pr_size,      // vms
+        TV2DOUBLE(info.pr_start),               // create time
+        (int) info.pr_lwp.pr_nice,              // nice
+        (int) info.pr_nlwp,                     // no. of threads
+        (int) status.pr_stat,                   // status code
+        (unsigned long long)info.pr_ttydev      // tty nr
+        );
+}
+
+
+/*
+ * Return process name and args as a Python tuple.
+ */
+static PyObject *
+psutil_proc_name_and_args(PyObject *self, PyObject *args) {
+    int pid;
+    char path[100];
+    psinfo_t info;
+    const char *procfs_path;
+    PyObject *py_name = NULL;
+    PyObject *py_args = NULL;
+    PyObject *py_retlist = NULL;
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+        return NULL;
+
+    py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
+    if (!py_name)
+        goto error;
+    py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
+    if (!py_args)
+        goto error;
+    py_retlist = Py_BuildValue("OO", py_name, py_args);
+    if (!py_retlist)
+        goto error;
+    Py_DECREF(py_name);
+    Py_DECREF(py_args);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_name);
+    Py_XDECREF(py_args);
+    Py_XDECREF(py_retlist);
+    return NULL;
+}
+
+
+#ifdef CURR_VERSION_THREAD
+/*
+ * Retrieves all threads used by process returning a list of tuples
+ * including thread id, user time and system time.
+ */
+static PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+    long pid;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    perfstat_thread_t *threadt = NULL;
+    perfstat_id_t id;
+    int i, rc, thread_count;
+
+    if (py_retlist == NULL)
+        return NULL;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+
+    /* Get the count of threads */
+    thread_count = perfstat_thread(NULL, NULL, sizeof(perfstat_thread_t), 0);
+    if (thread_count <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    /* Allocate enough memory */
+    threadt = (perfstat_thread_t *)calloc(thread_count,
+        sizeof(perfstat_thread_t));
+    if (threadt == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    strcpy(id.name, "");
+    rc = perfstat_thread(&id, threadt, sizeof(perfstat_thread_t),
+        thread_count);
+    if (rc <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < thread_count; i++) {
+        if (threadt[i].pid != pid)
+            continue;
+
+        py_tuple = Py_BuildValue("Idd",
+                                 threadt[i].tid,
+                                 threadt[i].ucpu_time,
+                                 threadt[i].scpu_time);
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+    free(threadt);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (threadt != NULL)
+        free(threadt);
+    return NULL;
+}
+#endif
+
+
+static PyObject *
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
+    long pid;
+    int rc;
+    perfstat_process_t procinfo;
+    perfstat_id_t id;
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+
+    snprintf(id.name, sizeof(id.name), "%ld", pid);
+    rc = perfstat_process(&id, &procinfo, sizeof(perfstat_process_t), 1);
+    if (rc <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("(KKKK)",
+                         procinfo.inOps,      // XXX always 0
+                         procinfo.outOps,
+                         procinfo.inBytes,    // XXX always 0
+                         procinfo.outBytes);
+}
+
+
+/*
+ * Return process user and system CPU times as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
+    int pid;
+    char path[100];
+    pstatus_t info;
+    const char *procfs_path;
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+    sprintf(path, "%s/%i/status", procfs_path, pid);
+    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+        return NULL;
+    // results are more precise than os.times()
+    return Py_BuildValue("dddd",
+                         TV2DOUBLE(info.pr_utime),
+                         TV2DOUBLE(info.pr_stime),
+                         TV2DOUBLE(info.pr_cutime),
+                         TV2DOUBLE(info.pr_cstime));
+}
+
+
+/*
+ * Return process uids/gids as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cred(PyObject *self, PyObject *args) {
+    int pid;
+    char path[100];
+    prcred_t info;
+    const char *procfs_path;
+
+    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+        return NULL;
+    sprintf(path, "%s/%i/cred", procfs_path, pid);
+    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+        return NULL;
+    return Py_BuildValue("iiiiii",
+                         info.pr_ruid, info.pr_euid, info.pr_suid,
+                         info.pr_rgid, info.pr_egid, info.pr_sgid);
+}
+
+
+/*
+ * Return process voluntary and involuntary context switches as a Python tuple.
+ */
+static PyObject *
+psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
+    PyObject *py_tuple = NULL;
+    pid32_t requested_pid;
+    pid32_t pid = 0;
+    int np = 0;
+    struct procentry64 *processes = (struct procentry64 *)NULL;
+    struct procentry64 *p;
+
+    if (! PyArg_ParseTuple(args, "i", &requested_pid))
+        return NULL;
+
+    processes = psutil_read_process_table(&np);
+    if (!processes)
+        return NULL;
+
+    /* Loop through processes */
+    for (p = processes; np > 0; np--, p++) {
+        pid = p->pi_pid;
+        if (requested_pid != pid)
+            continue;
+        py_tuple = Py_BuildValue("LL",
+            (long long) p->pi_ru.ru_nvcsw,    /* voluntary context switches */
+            (long long) p->pi_ru.ru_nivcsw);  /* involuntary */
+        free(processes);
+        return py_tuple;
+    }
+
+    /* finished iteration without finding requested pid */
+    free(processes);
+    return NoSuchProcess("");
+}
+
+
+/*
+ * Return users currently connected on the system.
+ */
+static PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+    struct utmpx *ut;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_tuple = NULL;
+    PyObject *py_username = NULL;
+    PyObject *py_tty = NULL;
+    PyObject *py_hostname = NULL;
+    PyObject *py_user_proc = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    setutxent();
+    while (NULL != (ut = getutxent())) {
+        if (ut->ut_type == USER_PROCESS)
+            py_user_proc = Py_True;
+        else
+            py_user_proc = Py_False;
+        py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
+        if (! py_username)
+            goto error;
+        py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
+        if (! py_tty)
+            goto error;
+        py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
+        if (! py_hostname)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOOfOi)",
+            py_username,              // username
+            py_tty,                   // tty
+            py_hostname,              // hostname
+            (float)ut->ut_tv.tv_sec,  // tstamp
+            py_user_proc,             // (bool) user process
+            ut->ut_pid                // process id
+        );
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_username);
+        Py_DECREF(py_tty);
+        Py_DECREF(py_hostname);
+        Py_DECREF(py_tuple);
+    }
+    endutxent();
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_username);
+    Py_XDECREF(py_tty);
+    Py_XDECREF(py_hostname);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (ut != NULL)
+        endutxent();
+    return NULL;
+}
+
+
+/*
+ * Return disk mounted partitions as a list of tuples including device,
+ * mount point and filesystem type.
+ */
+static PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+    FILE *file = NULL;
+    struct mntent * mt = NULL;
+    PyObject *py_dev = NULL;
+    PyObject *py_mountp = NULL;
+    PyObject *py_tuple = NULL;
+    PyObject *py_retlist = PyList_New(0);
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    file = setmntent(MNTTAB, "rb");
+    if (file == NULL) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    mt = getmntent(file);
+    while (mt != NULL) {
+        py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);
+        if (! py_dev)
+            goto error;
+        py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);
+        if (! py_mountp)
+            goto error;
+        py_tuple = Py_BuildValue(
+            "(OOss)",
+            py_dev,         // device
+            py_mountp,      // mount point
+            mt->mnt_type,   // fs type
+            mt->mnt_opts);  // options
+        if (py_tuple == NULL)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_dev);
+        Py_DECREF(py_mountp);
+        Py_DECREF(py_tuple);
+        mt = getmntent(file);
+    }
+    endmntent(file);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_dev);
+    Py_XDECREF(py_mountp);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (file != NULL)
+        endmntent(file);
+    return NULL;
+}
+
+
+/*
+ * Return a list of tuples for network I/O statistics.
+ */
+static PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+    perfstat_netinterface_t *statp = NULL;
+    int tot, i;
+    perfstat_id_t first;
+
+    PyObject *py_retdict = PyDict_New();
+    PyObject *py_ifc_info = NULL;
+
+    if (py_retdict == NULL)
+        return NULL;
+
+    /* check how many perfstat_netinterface_t structures are available */
+    tot = perfstat_netinterface(
+        NULL, NULL, sizeof(perfstat_netinterface_t), 0);
+    if (tot == 0) {
+        // no network interfaces - return empty dict
+        return py_retdict;
+    }
+    if (tot < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    statp = (perfstat_netinterface_t *)
+        malloc(tot * sizeof(perfstat_netinterface_t));
+    if (statp == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    strcpy(first.name, FIRST_NETINTERFACE);
+    tot = perfstat_netinterface(&first, statp,
+        sizeof(perfstat_netinterface_t), tot);
+    if (tot < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < tot; i++) {
+        py_ifc_info = Py_BuildValue("(KKKKKKKK)",
+            statp[i].obytes,      /* number of bytes sent on interface */
+            statp[i].ibytes,      /* number of bytes received on interface */
+            statp[i].opackets,    /* number of packets sent on interface */
+            statp[i].ipackets,    /* number of packets received on interface */
+            statp[i].ierrors,     /* number of input errors on interface */
+            statp[i].oerrors,     /* number of output errors on interface */
+            statp[i].if_iqdrops,  /* Dropped on input, this interface */
+            statp[i].xmitdrops    /* number of packets not transmitted */
+           );
+        if (!py_ifc_info)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, statp[i].name, py_ifc_info))
+            goto error;
+        Py_DECREF(py_ifc_info);
+    }
+
+    free(statp);
+    return py_retdict;
+
+error:
+    if (statp != NULL)
+        free(statp);
+    Py_XDECREF(py_ifc_info);
+    Py_DECREF(py_retdict);
+    return NULL;
+}
+
+
+static PyObject*
+psutil_net_if_stats(PyObject* self, PyObject* args) {
+    char *nic_name;
+    int sock = 0;
+    int ret;
+    int mtu;
+    struct ifreq ifr;
+    PyObject *py_is_up = NULL;
+    PyObject *py_retlist = NULL;
+
+    if (! PyArg_ParseTuple(args, "s", &nic_name))
+        return NULL;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1)
+        goto error;
+
+    strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+
+    // is up?
+    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+    if (ret == -1)
+        goto error;
+    if ((ifr.ifr_flags & IFF_UP) != 0)
+        py_is_up = Py_True;
+    else
+        py_is_up = Py_False;
+    Py_INCREF(py_is_up);
+
+    // MTU
+    ret = ioctl(sock, SIOCGIFMTU, &ifr);
+    if (ret == -1)
+        goto error;
+    mtu = ifr.ifr_mtu;
+
+    close(sock);
+    py_retlist = Py_BuildValue("[Oi]", py_is_up, mtu);
+    if (!py_retlist)
+        goto error;
+    Py_DECREF(py_is_up);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_is_up);
+    if (sock != 0)
+        close(sock);
+    PyErr_SetFromErrno(PyExc_OSError);
+    return NULL;
+}
+
+
+static PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+    float boot_time = 0.0;
+    struct utmpx *ut;
+
+    setutxent();
+    while (NULL != (ut = getutxent())) {
+        if (ut->ut_type == BOOT_TIME) {
+            boot_time = (float)ut->ut_tv.tv_sec;
+            break;
+        }
+    }
+    endutxent();
+    if (boot_time == 0.0) {
+        /* could not find BOOT_TIME in getutxent loop */
+        PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
+        return NULL;
+    }
+    return Py_BuildValue("f", boot_time);
+}
+
+
+/*
+ * Return a Python list of tuple representing per-cpu times
+ */
+static PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+    int ncpu, rc, i;
+    perfstat_cpu_t *cpu = NULL;
+    perfstat_id_t id;
+    PyObject *py_retlist = PyList_New(0);
+    PyObject *py_cputime = NULL;
+
+    if (py_retlist == NULL)
+        return NULL;
+
+    /* get the number of cpus in ncpu */
+    ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+    if (ncpu <= 0){
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    /* allocate enough memory to hold the ncpu structures */
+    cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+    if (cpu == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    strcpy(id.name, "");
+    rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+    if (rc <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < ncpu; i++) {
+        py_cputime = Py_BuildValue(
+            "(dddd)",
+            (double)cpu[i].user,
+            (double)cpu[i].sys,
+            (double)cpu[i].idle,
+            (double)cpu[i].wait);
+        if (!py_cputime)
+            goto error;
+        if (PyList_Append(py_retlist, py_cputime))
+            goto error;
+        Py_DECREF(py_cputime);
+    }
+    free(cpu);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cputime);
+    Py_DECREF(py_retlist);
+    if (cpu != NULL)
+        free(cpu);
+    return NULL;
+}
+
+
+/*
+ * Return disk IO statistics.
+ */
+static PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+    PyObject *py_retdict = PyDict_New();
+    PyObject *py_disk_info = NULL;
+    perfstat_disk_t *diskt = NULL;
+    perfstat_id_t id;
+    int i, rc, disk_count;
+
+    if (py_retdict == NULL)
+        return NULL;
+
+    /* Get the count of disks */
+    disk_count = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
+    if (disk_count <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    /* Allocate enough memory */
+    diskt = (perfstat_disk_t *)calloc(disk_count,
+        sizeof(perfstat_disk_t));
+    if (diskt == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    strcpy(id.name, FIRST_DISK);
+    rc = perfstat_disk(&id, diskt, sizeof(perfstat_disk_t),
+        disk_count);
+    if (rc <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < disk_count; i++) {
+        py_disk_info = Py_BuildValue(
+            "KKKKKK",
+            diskt[i].__rxfers,
+            diskt[i].xfers - diskt[i].__rxfers,
+            diskt[i].rblks * diskt[i].bsize,
+            diskt[i].wblks * diskt[i].bsize,
+            diskt[i].rserv / 1000 / 1000,  // from nano to milli secs
+            diskt[i].wserv / 1000 / 1000   // from nano to milli secs
+        );
+        if (py_disk_info == NULL)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, diskt[i].name,
+                                 py_disk_info))
+            goto error;
+        Py_DECREF(py_disk_info);
+    }
+    free(diskt);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_disk_info);
+    Py_DECREF(py_retdict);
+    if (diskt != NULL)
+        free(diskt);
+    return NULL;
+}
+
+
+/*
+ * Return virtual memory usage statistics.
+ */
+static PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+    int rc;
+    int pagesize = getpagesize();
+    perfstat_memory_total_t memory;
+
+    rc = perfstat_memory_total(
+        NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+    if (rc <= 0){
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("KKKKK",
+        (unsigned long long) memory.real_total * pagesize,
+        (unsigned long long) memory.real_avail * pagesize,
+        (unsigned long long) memory.real_free * pagesize,
+        (unsigned long long) memory.real_pinned * pagesize,
+        (unsigned long long) memory.real_inuse * pagesize
+    );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+static PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+    int rc;
+    int pagesize = getpagesize();
+    perfstat_memory_total_t memory;
+
+    rc = perfstat_memory_total(
+        NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+    if (rc <= 0){
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("KKKK",
+        (unsigned long long) memory.pgsp_total * pagesize,
+        (unsigned long long) memory.pgsp_free * pagesize,
+        (unsigned long long) memory.pgins * pagesize,
+        (unsigned long long) memory.pgouts * pagesize
+    );
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+    int ncpu, rc, i;
+    // perfstat_cpu_total_t doesn't have invol/vol cswitch, only pswitch
+    // which is apparently something else. We have to sum over all cpus
+    perfstat_cpu_t *cpu = NULL;
+    perfstat_id_t id;
+    u_longlong_t cswitches = 0;
+    u_longlong_t devintrs = 0;
+    u_longlong_t softintrs = 0;
+    u_longlong_t syscall = 0;
+
+    /* get the number of cpus in ncpu */
+    ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+    if (ncpu <= 0){
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    /* allocate enough memory to hold the ncpu structures */
+    cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+    if (cpu == NULL) {
+        PyErr_NoMemory();
+        goto error;
+    }
+
+    strcpy(id.name, "");
+    rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+    if (rc <= 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    for (i = 0; i < ncpu; i++) {
+        cswitches += cpu[i].invol_cswitch + cpu[i].vol_cswitch;
+        devintrs += cpu[i].devintrs;
+        softintrs += cpu[i].softintrs;
+        syscall += cpu[i].syscall;
+    }
+
+    free(cpu);
+
+    return Py_BuildValue(
+        "KKKK",
+        cswitches,
+        devintrs,
+        softintrs,
+        syscall
+    );
+
+error:
+    if (cpu != NULL)
+        free(cpu);
+    return NULL;
+}
+
+
+/*
+ * define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef
+PsutilMethods[] =
+{
+    // --- process-related functions
+    {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
+     "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
+    {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS,
+     "Return process name and args."},
+    {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
+     "Return process user and system CPU times."},
+    {"proc_cred", psutil_proc_cred, METH_VARARGS,
+     "Return process uids/gids."},
+#ifdef CURR_VERSION_THREAD
+    {"proc_threads", psutil_proc_threads, METH_VARARGS,
+     "Return process threads"},
+#endif
+    {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
+     "Get process I/O counters."},
+    {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
+     "Get process I/O counters."},
+
+    // --- system-related functions
+    {"users", psutil_users, METH_VARARGS,
+     "Return currently connected users."},
+    {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
+     "Return disk partitions."},
+    {"boot_time", psutil_boot_time, METH_VARARGS,
+     "Return system boot time in seconds since the EPOCH."},
+    {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
+     "Return system per-cpu times as a list of tuples"},
+    {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
+     "Return a Python dict of tuples for disk I/O statistics."},
+    {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
+     "Return system virtual memory usage statistics"},
+    {"swap_mem", psutil_swap_mem, METH_VARARGS,
+     "Return stats about swap memory, in bytes"},
+    {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
+     "Return a Python dict of tuples for network I/O statistics."},
+    {"net_connections", psutil_net_connections, METH_VARARGS,
+     "Return system-wide connections"},
+    {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+     "Return NIC stats (isup, mtu)"},
+    {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+     "Return CPU statistics"},
+
+    // --- others
+    {"set_testing", psutil_set_testing, METH_NOARGS,
+     "Set psutil in testing mode"},
+
+    {NULL, NULL, 0, NULL}
+};
+
+
+struct module_state {
+    PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+#else
+#define GETSTATE(m) (&_state)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+static int
+psutil_aix_traverse(PyObject *m, visitproc visit, void *arg) {
+    Py_VISIT(GETSTATE(m)->error);
+    return 0;
+}
+
+static int
+psutil_aix_clear(PyObject *m) {
+    Py_CLEAR(GETSTATE(m)->error);
+    return 0;
+}
+
+static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    "psutil_aix",
+    NULL,
+    sizeof(struct module_state),
+    PsutilMethods,
+    NULL,
+    psutil_aix_traverse,
+    psutil_aix_clear,
+    NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__psutil_aix(void)
+
+#else
+#define INITERROR return
+
+void init_psutil_aix(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+    PyObject *module = PyModule_Create(&moduledef);
+#else
+    PyObject *module = Py_InitModule("_psutil_aix", PsutilMethods);
+#endif
+    PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+
+    PyModule_AddIntConstant(module, "SIDL", SIDL);
+    PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+    PyModule_AddIntConstant(module, "SACTIVE", SACTIVE);
+    PyModule_AddIntConstant(module, "SSWAP", SSWAP);
+    PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+
+    PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED);
+    PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING);
+    PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT);
+    PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN);
+    PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED);
+    PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
+    PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RECEIVED);
+    PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1);
+    PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2);
+    PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
+    PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
+    PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+
+    psutil_setup();
+
+    if (module == NULL)
+        INITERROR;
+#if PY_MAJOR_VERSION >= 3
+    return module;
+#endif
+}
old mode 100644
new mode 100755
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/psutil/tests/test_aix.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola'
+# Copyright (c) 2017, Arnon Yaari
+# All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""AIX specific tests."""
+
+import re
+
+from psutil import AIX
+from psutil.tests import run_test_module_by_name
+from psutil.tests import sh
+from psutil.tests import unittest
+import psutil
+
+
+@unittest.skipIf(not AIX, "AIX only")
+class AIXSpecificTestCase(unittest.TestCase):
+
+    def test_virtual_memory(self):
+        out = sh('/usr/bin/svmon -O unit=KB')
+        re_pattern = "memory\s*"
+        for field in ("size inuse free pin virtual available mmode").split():
+            re_pattern += "(?P<%s>\S+)\s+" % (field,)
+        matchobj = re.search(re_pattern, out)
+
+        self.assertIsNotNone(
+            matchobj, "svmon command returned unexpected output")
+
+        KB = 1024
+        total = int(matchobj.group("size")) * KB
+        available = int(matchobj.group("available")) * KB
+        used = int(matchobj.group("inuse")) * KB
+        free = int(matchobj.group("free")) * KB
+
+        psutil_result = psutil.virtual_memory()
+
+        # MEMORY_TOLERANCE from psutil.tests is not enough. For some reason
+        # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance
+        # when compared to GBs.
+        MEMORY_TOLERANCE = 2 * KB * KB   # 2 MB
+        self.assertEqual(psutil_result.total, total)
+        self.assertAlmostEqual(
+            psutil_result.used, used, delta=MEMORY_TOLERANCE)
+        self.assertAlmostEqual(
+            psutil_result.available, available, delta=MEMORY_TOLERANCE)
+        self.assertAlmostEqual(
+            psutil_result.free, free, delta=MEMORY_TOLERANCE)
+
+    def test_swap_memory(self):
+        out = sh('/usr/sbin/lsps -a')
+        # From the man page, "The size is given in megabytes" so we assume
+        # we'll always have 'MB' in the result
+        # TODO maybe try to use "swap -l" to check "used" too, but its units
+        # are not guaranteed to be "MB" so parsing may not be consistent
+        matchobj = re.search("(?P<space>\S+)\s+"
+                             "(?P<vol>\S+)\s+"
+                             "(?P<vg>\S+)\s+"
+                             "(?P<size>\d+)MB", out)
+
+        self.assertIsNotNone(
+            matchobj, "lsps command returned unexpected output")
+
+        total_mb = int(matchobj.group("size"))
+        MB = 1024 ** 2
+        psutil_result = psutil.swap_memory()
+        # we divide our result by MB instead of multiplying the lsps value by
+        # MB because lsps may round down, so we round down too
+        self.assertEqual(int(psutil_result.total / MB), total_mb)
+
+    def test_cpu_stats(self):
+        out = sh('/usr/bin/mpstat -a')
+
+        re_pattern = "ALL\s*"
+        for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq "
+                      "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd "
+                      "sysc").split():
+            re_pattern += "(?P<%s>\S+)\s+" % (field,)
+        matchobj = re.search(re_pattern, out)
+
+        self.assertIsNotNone(
+            matchobj, "mpstat command returned unexpected output")
+
+        # numbers are usually in the millions so 1000 is ok for tolerance
+        CPU_STATS_TOLERANCE = 1000
+        psutil_result = psutil.cpu_stats()
+        self.assertAlmostEqual(
+            psutil_result.ctx_switches,
+            int(matchobj.group("cs")),
+            delta=CPU_STATS_TOLERANCE)
+        self.assertAlmostEqual(
+            psutil_result.syscalls,
+            int(matchobj.group("sysc")),
+            delta=CPU_STATS_TOLERANCE)
+        self.assertAlmostEqual(
+            psutil_result.interrupts,
+            int(matchobj.group("dev")),
+            delta=CPU_STATS_TOLERANCE)
+        self.assertAlmostEqual(
+            psutil_result.soft_interrupts,
+            int(matchobj.group("soft")),
+            delta=CPU_STATS_TOLERANCE)
+
+    def test_cpu_count_logical(self):
+        out = sh('/usr/bin/mpstat -a')
+        mpstat_lcpu = int(re.search("lcpu=(\d+)", out).group(1))
+        psutil_lcpu = psutil.cpu_count(logical=True)
+        self.assertEqual(mpstat_lcpu, psutil_lcpu)
+
+    def test_net_if_addrs_names(self):
+        out = sh('/etc/ifconfig -l')
+        ifconfig_names = set(out.split())
+        psutil_names = set(psutil.net_if_addrs().keys())
+        self.assertSetEqual(ifconfig_names, psutil_names)
+
+
+if __name__ == '__main__':
+    run_test_module_by_name(__file__)
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
new file mode 100755
--- /dev/null
+++ b/third_party/python/psutil/scripts/internal/print_announce.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Prints release announce based on HISTORY.rst file content.
+"""
+
+import os
+import re
+
+from psutil import __version__ as PRJ_VERSION
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+HISTORY = os.path.abspath(os.path.join(HERE, '../../HISTORY.rst'))
+
+PRJ_NAME = 'psutil'
+PRJ_URL_HOME = 'https://github.com/giampaolo/psutil'
+PRJ_URL_DOC = 'http://psutil.readthedocs.io'
+PRJ_URL_DOWNLOAD = 'https://pypi.python.org/pypi/psutil'
+PRJ_URL_WHATSNEW = \
+    'https://github.com/giampaolo/psutil/blob/master/HISTORY.rst'
+
+template = """\
+Hello all,
+I'm glad to announce the release of {prj_name} {prj_version}:
+{prj_urlhome}
+
+About
+=====
+
+psutil (process and system utilities) is a cross-platform library for \
+retrieving information on running processes and system utilization (CPU, \
+memory, disks, network) in Python. It is useful mainly for system \
+monitoring, profiling and limiting process resources and management of \
+running processes. It implements many functionalities offered by command \
+line tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, \
+nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It \
+currently supports Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD, NetBSD \
+and AIX, both 32-bit and 64-bit architectures, with Python versions from 2.6 \
+to 3.6. PyPy is also known to work.
+
+What's new
+==========
+
+{changes}
+
+Links
+=====
+
+- Home page: {prj_urlhome}
+- Download: {prj_urldownload}
+- Documentation: {prj_urldoc}
+- What's new: {prj_urlwhatsnew}
+
+--
+
+Giampaolo - http://grodola.blogspot.com
+"""
+
+
+def get_changes():
+    """Get the most recent changes for this release by parsing
+    HISTORY.rst file.
+    """
+    with open(HISTORY) as f:
+        lines = f.readlines()
+
+    block = []
+
+    # eliminate the part preceding the first block
+    for i, line in enumerate(lines):
+        line = lines.pop(0)
+        if line.startswith('===='):
+            break
+    lines.pop(0)
+
+    for i, line in enumerate(lines):
+        line = lines.pop(0)
+        line = line.rstrip()
+        if re.match("^- \d+_: ", line):
+            num, _, rest = line.partition(': ')
+            num = ''.join([x for x in num if x.isdigit()])
+            line = "- #%s: %s" % (num, rest)
+
+        if line.startswith('===='):
+            break
+        block.append(line)
+
+    # eliminate bottom empty lines
+    block.pop(-1)
+    while not block[-1]:
+        block.pop(-1)
+
+    return "\n".join(block)
+
+
+def main():
+    changes = get_changes()
+    print(template.format(
+        prj_name=PRJ_NAME,
+        prj_version=PRJ_VERSION,
+        prj_urlhome=PRJ_URL_HOME,
+        prj_urldownload=PRJ_URL_DOWNLOAD,
+        prj_urldoc=PRJ_URL_DOC,
+        prj_urlwhatsnew=PRJ_URL_WHATSNEW,
+        changes=changes,
+    ))
+
+
+if __name__ == '__main__':
+    main()
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755