From 21da6e70680e61cc29b0770dc3d763f0cc95844c Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 1 Aug 2015 03:17:36 -0700 Subject: [PATCH 01/34] #655: provide preliminary unicode tests for all process APIs returning strings (except username) --- test/_windows.py | 55 +++++++++++++++++++++++++++++++++++++++++++++ test/test_psutil.py | 12 +++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/test/_windows.py b/test/_windows.py index b7477bfeb..29da29cd6 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: UTF-8 -* # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. # Use of this source code is governed by a BSD-style license that can be @@ -9,14 +10,18 @@ import errno import os import platform +import shutil import signal import subprocess import sys +import tempfile import time import traceback +from psutil._compat import u from test_psutil import APPVEYOR, WINDOWS from test_psutil import get_test_subprocess, reap_children, unittest +from test_psutil import safe_remove, safe_rmdir, chdir import mock try: @@ -452,13 +457,63 @@ def test_zombies(self): self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) +class TestUnicode(unittest.TestCase): + + @classmethod + def setUpClass(cls): + with tempfile.NamedTemporaryFile() as f: + tdir = os.path.dirname(f.name) + cls.uexe = os.path.join(tdir, "psutil-è.exe") + + def setUp(self): + safe_remove(self.uexe) + reap_children() + + tearDown = setUp + + def test_proc_exe(self): + shutil.copyfile(sys.executable, self.uexe) + subp = get_test_subprocess(cmd=[self.uexe]) + p = psutil.Process(subp.pid) + self.assertEqual(os.path.basename(p.name()), u("psutil-è.exe")) + + def test_proc_name(self): + shutil.copyfile(sys.executable, self.uexe) + subp = get_test_subprocess(cmd=[self.uexe]) + self.assertEqual(psutil._psplatform.cext.proc_name(subp.pid), + u("psutil-è.exe")) + + def test_proc_cmdline(self): + shutil.copyfile(sys.executable, self.uexe) + subp = get_test_subprocess(cmd=[self.uexe]) + p = psutil.Process(subp.pid) + self.assertEqual(p.cmdline(), [self.uexe]) + + def test_proc_cwd(self): + tdir = tempfile.mkdtemp(prefix="psutil-è-") + self.addCleanup(safe_rmdir, tdir) + with chdir(tdir): + p = psutil.Process() + self.assertEqual(p.cwd(), tdir) + + def test_proc_open_files(self): + p = psutil.Process() + start = set(p.open_files()) + with open(self.uexe, 'w'): + new = set(p.open_files()) + path = (new - start).pop().path + self.assertEqual(path, self.uexe) + + def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) + test_suite.addTest(unittest.makeSuite(TestUnicode)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() + if __name__ == '__main__': if not main(): sys.exit(1) diff --git a/test/test_psutil.py b/test/test_psutil.py index 3b2e3587a..6d0f854ca 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -420,6 +420,17 @@ def safe_rmdir(dir): raise +@contextlib.contextmanager +def chdir(dirname): + """Context manager which temporarily changes the current directory.""" + curdir = os.getcwd() + try: + os.chdir(dirname) + yield + finally: + os.chdir(curdir) + + def call_until(fun, expr, timeout=GLOBAL_TIMEOUT): """Keep calling function for timeout secs and exit if eval() expression is True. @@ -2130,7 +2141,6 @@ def test_halfway_terminated_process(self): sproc = get_test_subprocess() p = psutil.Process(sproc.pid) p.terminate() - p.wait() if WINDOWS: wait_for_pid(p.pid) self.assertFalse(p.is_running()) From 2f6a0f810bd29080a3f3fee84dc6215642fa238b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 1 Aug 2015 03:20:16 -0700 Subject: [PATCH 02/34] wait for pid --- test/test_psutil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_psutil.py b/test/test_psutil.py index 6d0f854ca..e320db6a2 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2141,6 +2141,7 @@ def test_halfway_terminated_process(self): sproc = get_test_subprocess() p = psutil.Process(sproc.pid) p.terminate() + p.wait() if WINDOWS: wait_for_pid(p.pid) self.assertFalse(p.is_running()) From 249db99d6c09bbfd7d640a7da675e0166302903e Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 7 Aug 2015 14:44:46 +0200 Subject: [PATCH 03/34] fix appveyor --- test/test_psutil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_psutil.py b/test/test_psutil.py index ed93330ac..78c117c79 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -3007,6 +3007,7 @@ def test_check_presence(self): self.fail('no test defined for %r script' % os.path.join(EXAMPLES_DIR, name)) + @unittest.skipUnless(POSIX, "UNIX only") def test_executable(self): for name in os.listdir(EXAMPLES_DIR): if name.endswith('.py'): From e083f9ba768cf0843804378ca9f527cc0d5a837f Mon Sep 17 00:00:00 2001 From: sk6249 Date: Wed, 12 Aug 2015 17:23:22 +0800 Subject: [PATCH 04/34] Fix bug: incorrect network interface returned by net_io_counters(True) and net_if_addrs() [Bug description] When network interface contains non-ansii characters, such as Chinese characters, the network interface name returned by net_io_counters(True) and net_if_addrs() were truncated or blank string. [Solution] This fix ensures these two functions will return correct network interface names. --- psutil/_psutil_windows.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index eba974b7d..24b3370f8 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -138,6 +138,16 @@ typedef struct _MIB_UDP6TABLE_OWNER_PID { MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; } MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; +void wstr_to_str(char* pcstr, const wchar_t* pwstr, size_t len) { + int nlength = wcslen(pwstr); + int nbytes = WideCharToMultiByte(0, 0, pwstr, nlength, NULL, 0, NULL, NULL); + memset(pcstr, 0, len); + if (nbytes > len) { + nbytes = len; + } + WideCharToMultiByte(0, 0, pwstr, nlength, pcstr, nbytes, NULL, NULL); + return; +} PIP_ADAPTER_ADDRESSES psutil_get_nic_addresses() { @@ -2205,7 +2215,7 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { if (!py_nic_info) goto error; - sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + wstr_to_str(ifname,pCurrAddresses->FriendlyName, MAX_PATH); py_nic_name = PyUnicode_Decode( ifname, _tcslen(ifname), Py_FileSystemDefaultEncoding, "replace"); @@ -2865,7 +2875,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { while (pCurrAddresses) { pUnicast = pCurrAddresses->FirstUnicastAddress; - sprintf_s(ifname, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + wstr_to_str(ifname,pCurrAddresses->FriendlyName, MAX_PATH); // MAC address if (pCurrAddresses->PhysicalAddressLength != 0) { From 1c3a695ddb1237b3af51d96d59862c06af48daca Mon Sep 17 00:00:00 2001 From: sk6249 Date: Thu, 13 Aug 2015 11:29:29 +0800 Subject: [PATCH 05/34] Fix bug: incorrect network interface returned by net_io_counters and net_if_addrs [Bug description] When network interface contains non-ansii characters, such as Chinese characters, the network interface name returned by net_io_counters(True) and net_if_addrs() were truncated or blank. [Solution] This fix ensures these two functions will return correct network interface names. Use PyUnicode_FromWideChar to fix this issue. --- psutil/_psutil_windows.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 24b3370f8..d3faf50eb 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -138,17 +138,6 @@ typedef struct _MIB_UDP6TABLE_OWNER_PID { MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; } MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; -void wstr_to_str(char* pcstr, const wchar_t* pwstr, size_t len) { - int nlength = wcslen(pwstr); - int nbytes = WideCharToMultiByte(0, 0, pwstr, nlength, NULL, 0, NULL, NULL); - memset(pcstr, 0, len); - if (nbytes > len) { - nbytes = len; - } - WideCharToMultiByte(0, 0, pwstr, nlength, pcstr, nbytes, NULL, NULL); - return; -} - PIP_ADAPTER_ADDRESSES psutil_get_nic_addresses() { // allocate a 15 KB buffer to start with @@ -2169,7 +2158,6 @@ psutil_disk_usage(PyObject *self, PyObject *args) { */ static PyObject * psutil_net_io_counters(PyObject *self, PyObject *args) { - char ifname[MAX_PATH]; DWORD dwRetVal = 0; MIB_IFROW *pIfRow = NULL; PIP_ADAPTER_ADDRESSES pAddresses = NULL; @@ -2215,9 +2203,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { if (!py_nic_info) goto error; - wstr_to_str(ifname,pCurrAddresses->FriendlyName, MAX_PATH); - py_nic_name = PyUnicode_Decode( - ifname, _tcslen(ifname), Py_FileSystemDefaultEncoding, "replace"); + py_nic_name = PyUnicode_FromWideChar(pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); if (py_nic_name == NULL) goto error; @@ -2854,7 +2841,6 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { PCTSTR intRet; char *ptr; char buff[100]; - char ifname[MAX_PATH]; DWORD bufflen = 100; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; @@ -2864,6 +2850,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { PyObject *py_tuple = NULL; PyObject *py_address = NULL; PyObject *py_mac_address = NULL; + PyObject *py_nic_name = NULL; if (py_retlist == NULL) return NULL; @@ -2875,7 +2862,12 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { while (pCurrAddresses) { pUnicast = pCurrAddresses->FirstUnicastAddress; - wstr_to_str(ifname,pCurrAddresses->FriendlyName, MAX_PATH); + + py_nic_name = NULL; + py_nic_name = PyUnicode_FromWideChar(pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; // MAC address if (pCurrAddresses->PhysicalAddressLength != 0) { @@ -2906,8 +2898,8 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { Py_INCREF(Py_None); Py_INCREF(Py_None); py_tuple = Py_BuildValue( - "(siOOOO)", - ifname, + "(OiOOOO)", + py_nic_name, -1, // this will be converted later to AF_LINK py_mac_address, Py_None, // netmask (not supported) @@ -2960,8 +2952,8 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { Py_INCREF(Py_None); Py_INCREF(Py_None); py_tuple = Py_BuildValue( - "(siOOOO)", - ifname, + "(OiOOOO)", + py_nic_name, family, py_address, Py_None, // netmask (not supported) @@ -2979,7 +2971,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { pUnicast = pUnicast->Next; } } - + Py_DECREF(py_nic_name); pCurrAddresses = pCurrAddresses->Next; } @@ -2992,6 +2984,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { Py_DECREF(py_retlist); Py_XDECREF(py_tuple); Py_XDECREF(py_address); + Py_XDECREF(py_nic_name); return NULL; } From e251555084b9837a7e813255c1c55632e4274e3e Mon Sep 17 00:00:00 2001 From: stevenwinfield Date: Fri, 21 Aug 2015 13:17:13 +0100 Subject: [PATCH 06/34] Prevent enum clash when using Windows SDK v8.0 ProcessDebugPort and ProcessImageFileName are defined in PROCESSINFOCLASS in winternl.h - use the same workaround as ProcessBasicInformation, with underscores and #defines. --- psutil/arch/windows/ntextapi.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index d10432a3e..7f86c57ac 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -186,7 +186,7 @@ typedef enum _PROCESSINFOCLASS2 { ProcessTimes, ProcessBasePriority, ProcessRaisePriority, - ProcessDebugPort, + _ProcessDebugPort, ProcessExceptionPort, ProcessAccessToken, ProcessLdtInformation, @@ -207,7 +207,7 @@ typedef enum _PROCESSINFOCLASS2 { ProcessForegroundInformation, _ProcessWow64Information, /* added after XP+ */ - ProcessImageFileName, + _ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, ProcessBreakOnTermination, ProcessDebugObjectHandle, @@ -224,5 +224,7 @@ typedef enum _PROCESSINFOCLASS2 { #define PROCESSINFOCLASS PROCESSINFOCLASS2 #define ProcessBasicInformation _ProcessBasicInformation #define ProcessWow64Information _ProcessWow64Information +#define ProcessDebugPort _ProcessDebugPort +#define ProcessImageFileName _ProcessImageFileName #endif // __NTEXTAPI_H__ From 0487bcf74aba7832e08eb04ca1df8d4135c49efa Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 10:49:58 +0200 Subject: [PATCH 07/34] #672: update HISTORY/CREDITS --- CREDITS | 4 ++++ HISTORY.rst | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/CREDITS b/CREDITS index 3d7440e67..98adf16c4 100644 --- a/CREDITS +++ b/CREDITS @@ -313,3 +313,7 @@ I: 634 N: Bart van Kleef W: https://github.com/bkleef I: 664 + +N: Steven Winfield +W: https://github.com/stevenwinfield +I: 672 diff --git a/HISTORY.rst b/HISTORY.rst index 60574ce0d..b4405f2b7 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -15,6 +15,11 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #659: [Linux] compilation error on Suse 10. - #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef) +**Bug fixes** + +- #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by + Steven Winfield) + 3.1.1 - 2015-07-15 ================== From 58211221f70071531585665fcfa8ba4f21645d5f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 11:13:01 +0200 Subject: [PATCH 08/34] #670: update HISTORY/CREDITS --- CREDITS | 4 ++++ HISTORY.rst | 2 ++ psutil/_psutil_windows.c | 35 ++++++++++++++++++----------------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/CREDITS b/CREDITS index 98adf16c4..62fb347ea 100644 --- a/CREDITS +++ b/CREDITS @@ -317,3 +317,7 @@ I: 664 N: Steven Winfield W: https://github.com/stevenwinfield I: 672 + +N: sk6249 +W: https://github.com/sk6249 +I: 670 diff --git a/HISTORY.rst b/HISTORY.rst index b4405f2b7..def4bb3eb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -17,6 +17,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Bug fixes** +- #670: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names. + (patch by sk6249) - #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by Steven Winfield) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index d3faf50eb..504f8cccb 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -2203,8 +2203,9 @@ psutil_net_io_counters(PyObject *self, PyObject *args) { if (!py_nic_info) goto error; - py_nic_name = PyUnicode_FromWideChar(pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); if (py_nic_name == NULL) goto error; @@ -2850,7 +2851,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { PyObject *py_tuple = NULL; PyObject *py_address = NULL; PyObject *py_mac_address = NULL; - PyObject *py_nic_name = NULL; + PyObject *py_nic_name = NULL; if (py_retlist == NULL) return NULL; @@ -2862,12 +2863,12 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { while (pCurrAddresses) { pUnicast = pCurrAddresses->FirstUnicastAddress; - - py_nic_name = NULL; - py_nic_name = PyUnicode_FromWideChar(pCurrAddresses->FriendlyName, - wcslen(pCurrAddresses->FriendlyName)); - if (py_nic_name == NULL) - goto error; + + py_nic_name = NULL; + py_nic_name = PyUnicode_FromWideChar(pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; // MAC address if (pCurrAddresses->PhysicalAddressLength != 0) { @@ -2971,7 +2972,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { pUnicast = pUnicast->Next; } } - Py_DECREF(py_nic_name); + Py_DECREF(py_nic_name); pCurrAddresses = pCurrAddresses->Next; } @@ -2984,7 +2985,7 @@ psutil_net_if_addrs(PyObject *self, PyObject *args) { Py_DECREF(py_retlist); Py_XDECREF(py_tuple); Py_XDECREF(py_address); - Py_XDECREF(py_nic_name); + Py_XDECREF(py_nic_name); return NULL; } @@ -3065,14 +3066,14 @@ psutil_net_if_stats(PyObject *self, PyObject *args) { } // is up? - if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || + if((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED || pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) && pIfRow->dwAdminStatus == 1 ) { - py_is_up = Py_True; - } - else { - py_is_up = Py_False; - } + py_is_up = Py_True; + } + else { + py_is_up = Py_False; + } Py_INCREF(py_is_up); py_ifc_info = Py_BuildValue( From 90072f4317f6a2050774dc3b0b13700534c3cf0b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 02:45:01 -0700 Subject: [PATCH 09/34] enable unicode tests --- test/_windows.py | 1 + test/test_psutil.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/_windows.py b/test/_windows.py index 29da29cd6..0e57fe077 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -458,6 +458,7 @@ def test_zombies(self): class TestUnicode(unittest.TestCase): + # See: https://github.com/giampaolo/psutil/issues/655 @classmethod def setUpClass(cls): diff --git a/test/test_psutil.py b/test/test_psutil.py index 3d02a3071..3b480caaf 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -3103,8 +3103,9 @@ def main(): from _linux import LinuxSpecificTestCase as stc elif WINDOWS: from _windows import WindowsSpecificTestCase as stc - from _windows import TestDualProcessImplementation + from _windows import TestDualProcessImplementation, TestUnicode tests.append(TestDualProcessImplementation) + tests.append(TestUnicode) elif OSX: from _osx import OSXSpecificTestCase as stc elif BSD: From 9dd110bd406899ef9d2a0a49c7dad432e1f7c62b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 02:57:41 -0700 Subject: [PATCH 10/34] add unicode tests --- test/_windows.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/_windows.py b/test/_windows.py index 0e57fe077..93331789a 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -34,7 +34,7 @@ except ImportError: win32api = win32con = None -from psutil._compat import PY3, callable, long +from psutil._compat import PY3, callable, long, unicode import psutil @@ -476,11 +476,14 @@ def test_proc_exe(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) + self.assertIsInstance(p.name(), unicode) self.assertEqual(os.path.basename(p.name()), u("psutil-è.exe")) def test_proc_name(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) + self.assertIsInstance(psutil._psplatform.cext.proc_name(subp.pid), + unicode) self.assertEqual(psutil._psplatform.cext.proc_name(subp.pid), u("psutil-è.exe")) @@ -488,6 +491,7 @@ def test_proc_cmdline(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) + self.assertIsInstance("".join(p.cmdline()), unicode) self.assertEqual(p.cmdline(), [self.uexe]) def test_proc_cwd(self): @@ -495,6 +499,7 @@ def test_proc_cwd(self): self.addCleanup(safe_rmdir, tdir) with chdir(tdir): p = psutil.Process() + self.assertIsInstance(p.cwd(), unicode) self.assertEqual(p.cwd(), tdir) def test_proc_open_files(self): @@ -503,6 +508,7 @@ def test_proc_open_files(self): with open(self.uexe, 'w'): new = set(p.open_files()) path = (new - start).pop().path + self.assertIsInstance(path, unicode) self.assertEqual(path, self.uexe) From 064e65ede4c6fe0cc35887c4593de99d0243b7b0 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 03:03:01 -0700 Subject: [PATCH 11/34] #655: fix net_if_stats unicode error --- HISTORY.rst | 4 +--- psutil/_psutil_windows.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index def4bb3eb..1d1755948 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,11 +12,9 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #513: [Linux] fixed integer overflow for RLIM_INFINITY. - #641: [Windows] fixed many compilation warnings. (patch by Jeff Tang) +- #655: [Windows] net_if_stats unicode error in in case of non-ASCII NIC names. - #659: [Linux] compilation error on Suse 10. - #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef) - -**Bug fixes** - - #670: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names. (patch by sk6249) - #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 504f8cccb..07345f0fc 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -3004,10 +3004,10 @@ psutil_net_if_stats(PyObject *self, PyObject *args) { MIB_IFROW *pIfRow; PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; - char friendly_name[MAX_PATH]; char descr[MAX_PATH]; int ifname_found; + PyObject *py_nic_name = NULL; PyObject *py_retdict = PyDict_New(); PyObject *py_ifc_info = NULL; PyObject *py_is_up = NULL; @@ -3052,7 +3052,11 @@ psutil_net_if_stats(PyObject *self, PyObject *args) { while (pCurrAddresses) { sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description); if (lstrcmp(descr, pIfRow->bDescr) == 0) { - sprintf_s(friendly_name, MAX_PATH, "%wS", pCurrAddresses->FriendlyName); + py_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_nic_name == NULL) + goto error; ifname_found = 1; break; } @@ -3085,8 +3089,9 @@ psutil_net_if_stats(PyObject *self, PyObject *args) { ); if (!py_ifc_info) goto error; - if (PyDict_SetItemString(py_retdict, friendly_name, py_ifc_info)) + if (PyDict_SetItemString(py_retdict, py_nic_name, py_ifc_info)) goto error; + Py_DECREF(py_nic_name); Py_DECREF(py_ifc_info); } @@ -3097,6 +3102,7 @@ psutil_net_if_stats(PyObject *self, PyObject *args) { error: Py_XDECREF(py_is_up); Py_XDECREF(py_ifc_info); + Py_XDECREF(py_nic_name); Py_DECREF(py_retdict); if (pIfTable != NULL) free(pIfTable); From 0847640d3ed3108051377042cdf1b1d811cd07c5 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 06:03:44 -0700 Subject: [PATCH 12/34] #655: have Process.name() fallback method return unicode instead of str --- HISTORY.rst | 11 ++++++++++- psutil/_psutil_windows.c | 11 ++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 1d1755948..9e7188b4a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,12 +7,21 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #648: CI test integration for OSX. (patch by Jeff Tang) - #663: net_if_addrs() now returns point-to-point addresses (for VPNs). +- #655: [Windows] str/uniocde unification. Different methods returning a string + now return unicode on both Python 2 and 3: + - net_if_stats + - net_io_counters + - users + - Process.username + - Process.name + - Process.cmdline **Bug fixes** - #513: [Linux] fixed integer overflow for RLIM_INFINITY. - #641: [Windows] fixed many compilation warnings. (patch by Jeff Tang) -- #655: [Windows] net_if_stats unicode error in in case of non-ASCII NIC names. +- #655: [Windows] net_if_stats UnicodeDecodeError in case of non-ASCII NIC + names. - #659: [Linux] compilation error on Suse 10. - #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef) - #670: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names. diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 07345f0fc..a6bf28bc1 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -638,7 +638,7 @@ static PyObject * psutil_proc_name(PyObject *self, PyObject *args) { long pid; int ok; - PROCESSENTRY32 pentry; + PROCESSENTRY32W pentry; HANDLE hSnapShot; if (! PyArg_ParseTuple(args, "l", &pid)) @@ -648,8 +648,8 @@ psutil_proc_name(PyObject *self, PyObject *args) { PyErr_SetFromWindowsErr(0); return NULL; } - pentry.dwSize = sizeof(PROCESSENTRY32); - ok = Process32First(hSnapShot, &pentry); + pentry.dwSize = sizeof(PROCESSENTRY32W); + ok = Process32FirstW(hSnapShot, &pentry); if (! ok) { CloseHandle(hSnapShot); PyErr_SetFromWindowsErr(0); @@ -658,9 +658,10 @@ psutil_proc_name(PyObject *self, PyObject *args) { while (ok) { if (pentry.th32ProcessID == pid) { CloseHandle(hSnapShot); - return Py_BuildValue("s", pentry.szExeFile); + return PyUnicode_FromWideChar( + pentry.szExeFile, wcslen(pentry.szExeFile)); } - ok = Process32Next(hSnapShot, &pentry); + ok = Process32NextW(hSnapShot, &pentry); } CloseHandle(hSnapShot); From 8d946e075f5a2d760b083a7cfacd5d01bfb9f72d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 07:43:29 -0700 Subject: [PATCH 13/34] #650: stringify name and exe --- docs/index.rst | 6 ++++++ psutil/__init__.py | 2 +- psutil/_pswindows.py | 16 +++++++++++++--- test/_windows.py | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 4a2a5b941..9848b0680 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -681,12 +681,18 @@ Process class The process name. The return value is cached after first call. + *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII name the + returned type is unicode instead of str. + .. method:: exe() The process executable as an absolute path. On some systems this may also be an empty string. The return value is cached after first call. + *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII path the + returned type is unicode instead of str. + .. method:: cmdline() The command line this process has been called with. diff --git a/psutil/__init__.py b/psutil/__init__.py index 385b80a4a..166883620 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -158,7 +158,7 @@ ] __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "3.1.2" +__version__ = "3.2.0" version_info = tuple([int(num) for num in __version__.split('.')]) AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 2d8babb19..ca48120f0 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -234,6 +234,16 @@ def users(): return retlist +def py2_stringify(s): + if PY3: + return s + else: + try: + return str(s) + except UnicodeEncodeError: + return s + + pids = cext.pids pid_exists = cext.pid_exists net_io_counters = cext.net_io_counters @@ -287,9 +297,9 @@ def name(self): try: # Note: this will fail with AD for most PIDs owned # by another user but it's faster. - return os.path.basename(self.exe()) + return py2_stringify(os.path.basename(self.exe())) except AccessDenied: - return cext.proc_name(self.pid) + return py2_stringify(cext.proc_name(self.pid)) @wrap_exceptions def exe(self): @@ -301,7 +311,7 @@ def exe(self): # see https://github.com/giampaolo/psutil/issues/528 if self.pid in (0, 4): raise AccessDenied(self.pid, self._name) - return _convert_raw_path(cext.proc_exe(self.pid)) + return py2_stringify(_convert_raw_path(cext.proc_exe(self.pid))) @wrap_exceptions def cmdline(self): diff --git a/test/_windows.py b/test/_windows.py index 93331789a..5d6fd366f 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -292,7 +292,7 @@ def test_name_always_available(self): for p in psutil.process_iter(): try: p.name() - except psutil.NoSuchProcess(): + except psutil.NoSuchProcess: pass From d592453854a6742c2e9efff25b5203d544853d7a Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 08:02:32 -0700 Subject: [PATCH 14/34] #650: Process.cwd() return unicode for non ASCII paths --- docs/index.rst | 3 +++ psutil/_psutil_windows.c | 35 +++++++---------------------------- psutil/_pswindows.py | 2 +- test/_windows.py | 2 +- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9848b0680..69edcead7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -744,6 +744,9 @@ Process class The process current working directory as an absolute path. + *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII path the + returned type is unicode instead of str. + .. method:: username() The name of the user that owns the process. On UNIX this is calculated by diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index a6bf28bc1..43ef0d472 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -941,9 +941,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { PVOID rtlUserProcParamsAddress; UNICODE_STRING currentDirectory; WCHAR *currentDirectoryContent = NULL; - PyObject *returnPyObj = NULL; - PyObject *cwd_from_wchar = NULL; - PyObject *cwd = NULL; + PyObject *py_cwd = NULL; if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; @@ -1024,36 +1022,17 @@ psutil_proc_cwd(PyObject *self, PyObject *args) { // currentDirectory.Length is in bytes currentDirectoryContent[(currentDirectory.Length / sizeof(WCHAR))] = '\0'; - // convert wchar array to a Python unicode string, and then to UTF8 - cwd_from_wchar = PyUnicode_FromWideChar(currentDirectoryContent, - wcslen(currentDirectoryContent)); - if (cwd_from_wchar == NULL) + // convert wchar array to a Python unicode string + py_cwd = PyUnicode_FromWideChar( + currentDirectoryContent, wcslen(currentDirectoryContent)); + if (py_cwd == NULL) goto error; - -#if PY_MAJOR_VERSION >= 3 - cwd = PyUnicode_FromObject(cwd_from_wchar); -#else - cwd = PyUnicode_AsUTF8String(cwd_from_wchar); -#endif - if (cwd == NULL) - goto error; - - // decrement the reference count on our temp unicode str to avoid - // mem leak - returnPyObj = Py_BuildValue("N", cwd); - if (!returnPyObj) - goto error; - - Py_DECREF(cwd_from_wchar); - CloseHandle(processHandle); free(currentDirectoryContent); - return returnPyObj; + return py_cwd; error: - Py_XDECREF(cwd_from_wchar); - Py_XDECREF(cwd); - Py_XDECREF(returnPyObj); + Py_XDECREF(py_cwd); if (currentDirectoryContent != NULL) free(currentDirectoryContent); if (processHandle != NULL) diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index ca48120f0..479cf7706 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -440,7 +440,7 @@ def cwd(self): # return a normalized pathname since the native C function appends # "\\" at the and of the path path = cext.proc_cwd(self.pid) - return os.path.normpath(path) + return py2_stringify(os.path.normpath(path)) @wrap_exceptions def open_files(self): diff --git a/test/_windows.py b/test/_windows.py index 5d6fd366f..8f2c7a524 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -495,7 +495,7 @@ def test_proc_cmdline(self): self.assertEqual(p.cmdline(), [self.uexe]) def test_proc_cwd(self): - tdir = tempfile.mkdtemp(prefix="psutil-è-") + tdir = tempfile.mkdtemp(prefix=u("psutil-è-")) self.addCleanup(safe_rmdir, tdir) with chdir(tdir): p = psutil.Process() From b9823dbb3071d902753e713b0c32d027e73a7946 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 26 Aug 2015 08:58:03 -0700 Subject: [PATCH 15/34] #650: make cmdline() handle unicode on python 2 --- docs/index.rst | 4 +++ psutil/_pswindows.py | 15 +++++++- psutil/arch/windows/process_info.c | 55 +++++++++--------------------- test/_windows.py | 6 ++-- 4 files changed, 38 insertions(+), 42 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 69edcead7..f7dad7487 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -697,6 +697,10 @@ Process class The command line this process has been called with. + *Changed in 3.2.0:* (Windows, Python 2) in case one or more parts of the + cmdline contains non ASCII characters the returned type is a list of + unicode strings. + .. method:: create_time() The process creation time as a floating point number expressed in seconds diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 479cf7706..3c86d9947 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -315,7 +315,20 @@ def exe(self): @wrap_exceptions def cmdline(self): - return cext.proc_cmdline(self.pid) + ret = cext.proc_cmdline(self.pid) + if PY3: + return ret + else: + # On Python 2, if one or more bits of the cmdline is unicode + # we return a list of unicode strings. + new = [] + for x in ret: + x = py2_stringify(x) + if isinstance(x, unicode): + return ret + else: + new.append(x) + return new def ppid(self): try: diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c index b925b05f6..848604132 100644 --- a/psutil/arch/windows/process_info.c +++ b/psutil/arch/windows/process_info.c @@ -217,9 +217,8 @@ psutil_get_arg_list(long pid) { PVOID rtlUserProcParamsAddress; UNICODE_STRING commandLine; WCHAR *commandLineContents = NULL; - PyObject *arg = NULL; - PyObject *arg_from_wchar = NULL; - PyObject *argList = NULL; + PyObject *py_arg = NULL; + PyObject *py_retlist = NULL; hProcess = psutil_handle_from_pid(pid); if (hProcess == NULL) @@ -274,48 +273,27 @@ psutil_get_arg_list(long pid) { // commandLine.Length is in bytes. commandLineContents[(commandLine.Length / sizeof(WCHAR))] = '\0'; - // attempt tp parse the command line using Win32 API, fall back + // attempt to parse the command line using Win32 API, fall back // on string cmdline version otherwise szArglist = CommandLineToArgvW(commandLineContents, &nArgs); - if (NULL == szArglist) { - // failed to parse arglist - // encode as a UTF8 Python string object from WCHAR string - arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, - commandLine.Length / 2); - if (arg_from_wchar == NULL) - goto error; -#if PY_MAJOR_VERSION >= 3 - argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar)); -#else - argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); -#endif - if (!argList) - goto error; + if (szArglist == NULL) { + PyErr_SetFromWindowsErr(0); + goto error; } else { // arglist parsed as array of UNICODE_STRING, so convert each to // Python string object and add to arg list - argList = Py_BuildValue("[]"); - if (argList == NULL) + py_retlist = Py_BuildValue("[]"); + if (py_retlist == NULL) goto error; for (i = 0; i < nArgs; i++) { - arg_from_wchar = NULL; - arg = NULL; - arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], - wcslen(szArglist[i])); - if (arg_from_wchar == NULL) - goto error; -#if PY_MAJOR_VERSION >= 3 - arg = PyUnicode_FromObject(arg_from_wchar); -#else - arg = PyUnicode_AsUTF8String(arg_from_wchar); -#endif - if (arg == NULL) + py_arg = PyUnicode_FromWideChar( + szArglist[i], wcslen(szArglist[i])); + if (py_arg == NULL) goto error; - Py_XDECREF(arg_from_wchar); - if (PyList_Append(argList, arg)) + if (PyList_Append(py_retlist, py_arg)) goto error; - Py_XDECREF(arg); + Py_XDECREF(py_arg); } } @@ -323,12 +301,11 @@ psutil_get_arg_list(long pid) { LocalFree(szArglist); free(commandLineContents); CloseHandle(hProcess); - return argList; + return py_retlist; error: - Py_XDECREF(arg); - Py_XDECREF(arg_from_wchar); - Py_XDECREF(argList); + Py_XDECREF(py_arg); + Py_XDECREF(py_retlist); if (hProcess != NULL) CloseHandle(hProcess); if (commandLineContents != NULL) diff --git a/test/_windows.py b/test/_windows.py index 8f2c7a524..701017262 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -491,8 +491,10 @@ def test_proc_cmdline(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) - self.assertIsInstance("".join(p.cmdline()), unicode) - self.assertEqual(p.cmdline(), [self.uexe]) + self.assertIsInstance(u("").join(p.cmdline()), unicode) + uexe = self.uexe if PY3 else \ + unicode(self.uexe, sys.getfilesystemencoding()) + self.assertEqual(p.cmdline(), [uexe]) def test_proc_cwd(self): tdir = tempfile.mkdtemp(prefix=u("psutil-è-")) From 5176c50dc4b67de5d760636a70444ed21c807dc4 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 04:29:11 -0700 Subject: [PATCH 16/34] always return encoded strings instead of unicode --- HISTORY.rst | 18 ++++++++++-------- docs/index.rst | 13 ------------- psutil/_pswindows.py | 37 ++++++++++++++++--------------------- test/_windows.py | 26 ++++++++++++-------------- 4 files changed, 38 insertions(+), 56 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9e7188b4a..f283932ab 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,14 +7,16 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #648: CI test integration for OSX. (patch by Jeff Tang) - #663: net_if_addrs() now returns point-to-point addresses (for VPNs). -- #655: [Windows] str/uniocde unification. Different methods returning a string - now return unicode on both Python 2 and 3: - - net_if_stats - - net_io_counters - - users - - Process.username - - Process.name - - Process.cmdline +- #655: [Windows] different issues regarding unicode handling were fixed. On + Python 2 all APIs returning a string will now return an encoded version of it + by using sys.getfilesystemencoding() codec. The APIs involved are: + - psutil.net_if_stats + - psutil.net_if_addrs + - psutil.net_io_counters + - psutil.users + - psutil.Process.username + - psutil.Process.name + - psutil.Process.cmdline **Bug fixes** diff --git a/docs/index.rst b/docs/index.rst index f7dad7487..4a2a5b941 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -681,26 +681,16 @@ Process class The process name. The return value is cached after first call. - *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII name the - returned type is unicode instead of str. - .. method:: exe() The process executable as an absolute path. On some systems this may also be an empty string. The return value is cached after first call. - *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII path the - returned type is unicode instead of str. - .. method:: cmdline() The command line this process has been called with. - *Changed in 3.2.0:* (Windows, Python 2) in case one or more parts of the - cmdline contains non ASCII characters the returned type is a list of - unicode strings. - .. method:: create_time() The process creation time as a floating point number expressed in seconds @@ -748,9 +738,6 @@ Process class The process current working directory as an absolute path. - *Changed in 3.2.0:* (Windows, Python 2) in case of non ASCII path the - returned type is unicode instead of str. - .. method:: username() The name of the user that owns the process. On UNIX this is calculated by diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 3c86d9947..3e20d3be7 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -234,13 +234,15 @@ def users(): return retlist -def py2_stringify(s): - if PY3: +def py2_strencode(s, encoding=sys.getfilesystemencoding()): + if PY3 or isinstance(s, str): return s else: try: - return str(s) + return s.encode(encoding) except UnicodeEncodeError: + # Filesystem codec failed, return the plain unicode + # string (this should never happen). return s @@ -297,9 +299,9 @@ def name(self): try: # Note: this will fail with AD for most PIDs owned # by another user but it's faster. - return py2_stringify(os.path.basename(self.exe())) + return py2_strencode(os.path.basename(self.exe())) except AccessDenied: - return py2_stringify(cext.proc_name(self.pid)) + return py2_strencode(cext.proc_name(self.pid)) @wrap_exceptions def exe(self): @@ -311,7 +313,7 @@ def exe(self): # see https://github.com/giampaolo/psutil/issues/528 if self.pid in (0, 4): raise AccessDenied(self.pid, self._name) - return py2_stringify(_convert_raw_path(cext.proc_exe(self.pid))) + return py2_strencode(_convert_raw_path(cext.proc_exe(self.pid))) @wrap_exceptions def cmdline(self): @@ -319,16 +321,7 @@ def cmdline(self): if PY3: return ret else: - # On Python 2, if one or more bits of the cmdline is unicode - # we return a list of unicode strings. - new = [] - for x in ret: - x = py2_stringify(x) - if isinstance(x, unicode): - return ret - else: - new.append(x) - return new + return [py2_strencode(s) for s in ret] def ppid(self): try: @@ -453,13 +446,13 @@ def cwd(self): # return a normalized pathname since the native C function appends # "\\" at the and of the path path = cext.proc_cwd(self.pid) - return py2_stringify(os.path.normpath(path)) + return py2_strencode(os.path.normpath(path)) @wrap_exceptions def open_files(self): if self.pid in (0, 4): return [] - retlist = [] + ret = set() # Filenames come in in native format like: # "\Device\HarddiskVolume1\Windows\systemew\file.txt" # Convert the first part in the corresponding drive letter @@ -467,10 +460,12 @@ def open_files(self): raw_file_names = cext.proc_open_files(self.pid) for _file in raw_file_names: _file = _convert_raw_path(_file) - if isfile_strict(_file) and _file not in retlist: + if isfile_strict(_file): + if not PY3: + _file = py2_strencode(_file) ntuple = _common.popenfile(_file, -1) - retlist.append(ntuple) - return retlist + ret.add(ntuple) + return list(ret) @wrap_exceptions def connections(self, kind='inet'): diff --git a/test/_windows.py b/test/_windows.py index 701017262..0d2b2e159 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -476,32 +476,30 @@ def test_proc_exe(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) - self.assertIsInstance(p.name(), unicode) - self.assertEqual(os.path.basename(p.name()), u("psutil-è.exe")) + self.assertIsInstance(p.name(), str) + self.assertEqual(os.path.basename(p.name()), "psutil-è.exe") def test_proc_name(self): + from psutil._pswindows import py2_strencode shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) - self.assertIsInstance(psutil._psplatform.cext.proc_name(subp.pid), - unicode) - self.assertEqual(psutil._psplatform.cext.proc_name(subp.pid), - u("psutil-è.exe")) + self.assertEqual( + py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)), + "psutil-è.exe") def test_proc_cmdline(self): shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) - self.assertIsInstance(u("").join(p.cmdline()), unicode) - uexe = self.uexe if PY3 else \ - unicode(self.uexe, sys.getfilesystemencoding()) - self.assertEqual(p.cmdline(), [uexe]) + self.assertIsInstance("".join(p.cmdline()), str) + self.assertEqual(p.cmdline(), [self.uexe]) def test_proc_cwd(self): - tdir = tempfile.mkdtemp(prefix=u("psutil-è-")) + tdir = tempfile.mkdtemp(prefix="psutil-è-") self.addCleanup(safe_rmdir, tdir) with chdir(tdir): p = psutil.Process() - self.assertIsInstance(p.cwd(), unicode) + self.assertIsInstance(p.cwd(), str) self.assertEqual(p.cwd(), tdir) def test_proc_open_files(self): @@ -510,8 +508,8 @@ def test_proc_open_files(self): with open(self.uexe, 'w'): new = set(p.open_files()) path = (new - start).pop().path - self.assertIsInstance(path, unicode) - self.assertEqual(path, self.uexe) + self.assertIsInstance(path, str) + self.assertEqual(path.lower(), self.uexe.lower()) def main(): From c3bd8b90f66d1dec98c099cd651f3de3a0381b30 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 04:47:40 -0700 Subject: [PATCH 17/34] str-encode NIC names --- psutil/_pswindows.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 3e20d3be7..2d92486f8 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -113,6 +113,18 @@ def _convert_raw_path(s): return os.path.join(driveletter, s[len(rawdrive):]) +def py2_strencode(s, encoding=sys.getfilesystemencoding()): + if PY3 or isinstance(s, str): + return s + else: + try: + return s.encode(encoding) + except UnicodeEncodeError: + # Filesystem codec failed, return the plain unicode + # string (this should never happen). + return s + + # --- public functions @@ -216,6 +228,7 @@ def net_connections(kind, _pid=-1): def net_if_stats(): ret = cext.net_if_stats() for name, items in ret.items(): + name = py2_strencode(name) isup, duplex, speed, mtu = items if hasattr(_common, 'NicDuplex'): duplex = _common.NicDuplex(duplex) @@ -223,35 +236,36 @@ def net_if_stats(): return ret +def net_io_counters(): + ret = cext.net_io_counters() + return dict([(py2_strencode(k), v) for k, v in ret.items()]) + + +def net_if_addrs(): + ret = [] + for items in cext.net_if_addrs(): + items = list(items) + items[0] = py2_strencode(items[0]) + ret.append(items) + return ret + + def users(): """Return currently connected users as a list of namedtuples.""" retlist = [] rawlist = cext.users() for item in rawlist: user, hostname, tstamp = item + user = py2_strencode(user) nt = _common.suser(user, None, hostname, tstamp) retlist.append(nt) return retlist -def py2_strencode(s, encoding=sys.getfilesystemencoding()): - if PY3 or isinstance(s, str): - return s - else: - try: - return s.encode(encoding) - except UnicodeEncodeError: - # Filesystem codec failed, return the plain unicode - # string (this should never happen). - return s - - pids = cext.pids pid_exists = cext.pid_exists -net_io_counters = cext.net_io_counters disk_io_counters = cext.disk_io_counters ppid_map = cext.ppid_map # not meant to be public -net_if_addrs = cext.net_if_addrs def wrap_exceptions(fun): From 2c16ca797d286ab550726ea6c82a7378a10d6910 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 07:31:10 -0700 Subject: [PATCH 18/34] fix #644: add support for CTRL_* signals on Windows --- HISTORY.rst | 4 +++- docs/index.rst | 10 +++++++--- psutil/__init__.py | 8 +++++++- psutil/_pswindows.py | 4 ++++ test/_windows.py | 13 +++++++++++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index f283932ab..d4aea085f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,10 +1,12 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -3.1.2 - XXXX-XX-XX +3.2.0 - XXXX-XX-XX ================== **Enhancements** +- #644: [Windows] added support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals + to use with Process.send_signal(). - #648: CI test integration for OSX. (patch by Jeff Tang) - #663: net_if_addrs() now returns point-to-point addresses (for VPNs). - #655: [Windows] different issues regarding unicode handling were fixed. On diff --git a/docs/index.rst b/docs/index.rst index 4a2a5b941..a27ac2fb9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -457,7 +457,7 @@ Network *New in 3.0.0* - *Changed in 3.1.2:* *ptp* field was added. + *Changed in 3.2.0:* *ptp* field was added. .. function:: net_if_stats() @@ -1183,10 +1183,14 @@ Process class Send a signal to process (see `signal module `__ constants) pre-emptively checking whether PID has been reused. - This is the same as ``os.kill(pid, sig)``. - On Windows only **SIGTERM** is valid and is treated as an alias for + On UNIX this is the same as ``os.kill(pid, sig)``. + On Windows only **SIGTERM**, **CTRL_C_EVENT ** and **CTRL_BREAK_EVENT** + signals are supported and **SIGTERM** is treated as an alias for :meth:`kill()`. + *Changed in 3.2.0:* support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals + was added. + .. method:: suspend() Suspend process execution with **SIGSTOP** signal pre-emptively checking diff --git a/psutil/__init__.py b/psutil/__init__.py index 166883620..d85f86d48 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1039,8 +1039,14 @@ def send_signal(self, sig): else: if sig == signal.SIGTERM: self._proc.kill() + # py >= 2.7 + elif sig in (getattr(signal, "CTRL_C_EVENT", object()), + getattr(signal, "CTRL_BREAK_EVENT", object())): + self._proc.send_signal(sig) else: - raise ValueError("only SIGTERM is supported on Windows") + raise ValueError( + "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " + "are supported on Windows") @_assert_pid_not_reused def suspend(self): diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 2d92486f8..30b4d9fa7 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -387,6 +387,10 @@ def memory_maps(self): def kill(self): return cext.proc_kill(self.pid) + @wrap_exceptions + def send_signal(self, sig): + os.kill(self.pid, sig) + @wrap_exceptions def wait(self, timeout=None): if timeout is None: diff --git a/test/_windows.py b/test/_windows.py index 0d2b2e159..885992be8 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -295,6 +295,19 @@ def test_name_always_available(self): except psutil.NoSuchProcess: pass + @unittest.skipUnless(sys.version_info >= (2, 7), + "CTRL_* signals not supported") + def test_ctrl_signals(self): + p = psutil.Process(get_test_subprocess().pid) + p.send_signal(signal.CTRL_C_EVENT) + p.send_signal(signal.CTRL_BREAK_EVENT) + p.kill() + p.wait() + self.assertRaises(psutil.NoSuchProcess, + p.send_signal, signal.CTRL_C_EVENT) + self.assertRaises(psutil.NoSuchProcess, + p.send_signal, signal.CTRL_BREAK_EVENT) + @unittest.skipUnless(WINDOWS, "not a Windows system") class TestDualProcessImplementation(unittest.TestCase): From 62f9c4de4d4f4194d1338615f5ea3b5c7b8177c1 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 16:31:35 +0200 Subject: [PATCH 19/34] make flake8 happy --- test/_windows.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/_windows.py b/test/_windows.py index 0d2b2e159..57b7dd262 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -18,7 +18,6 @@ import time import traceback -from psutil._compat import u from test_psutil import APPVEYOR, WINDOWS from test_psutil import get_test_subprocess, reap_children, unittest from test_psutil import safe_remove, safe_rmdir, chdir @@ -34,7 +33,7 @@ except ImportError: win32api = win32con = None -from psutil._compat import PY3, callable, long, unicode +from psutil._compat import PY3, callable, long import psutil From b8adcd733d35d4e2351aee2cb7e6ac120c835825 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 18:07:16 +0200 Subject: [PATCH 20/34] #659: [Linux] compilation error on Suse 10. (patch by maozguttman) --- CREDITS | 4 ++++ HISTORY.rst | 14 +++++++------- psutil/_psutil_linux.c | 10 +++++++++- setup.py | 23 ++++++++++++++++++++++- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CREDITS b/CREDITS index 62fb347ea..60faa7497 100644 --- a/CREDITS +++ b/CREDITS @@ -321,3 +321,7 @@ I: 672 N: sk6249 W: https://github.com/sk6249 I: 670 + +N: maozguttman +W: https://github.com/maozguttman +I: 659 diff --git a/HISTORY.rst b/HISTORY.rst index d4aea085f..5c00f9bf0 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,25 +8,25 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #644: [Windows] added support for CTRL_C_EVENT and CTRL_BREAK_EVENT signals to use with Process.send_signal(). - #648: CI test integration for OSX. (patch by Jeff Tang) -- #663: net_if_addrs() now returns point-to-point addresses (for VPNs). +- #663: [UNIX] net_if_addrs() now returns point-to-point (VPNs) addresses. - #655: [Windows] different issues regarding unicode handling were fixed. On Python 2 all APIs returning a string will now return an encoded version of it by using sys.getfilesystemencoding() codec. The APIs involved are: - - psutil.net_if_stats - psutil.net_if_addrs + - psutil.net_if_stats - psutil.net_io_counters - - psutil.users - - psutil.Process.username - - psutil.Process.name - psutil.Process.cmdline + - psutil.Process.name + - psutil.Process.username + - psutil.users **Bug fixes** - #513: [Linux] fixed integer overflow for RLIM_INFINITY. - #641: [Windows] fixed many compilation warnings. (patch by Jeff Tang) -- #655: [Windows] net_if_stats UnicodeDecodeError in case of non-ASCII NIC +- #655: [Windows] net_if_stats UnicodeDecodeError in case of non-ASCII NIC names. -- #659: [Linux] compilation error on Suse 10. +- #659: [Linux] compilation error on Suse 10. (patch by maozguttman) - #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef) - #670: [Windows] segfgault of net_if_addrs() in case of non-ASCII NIC names. (patch by sk6249) diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 697c81b09..5c63ef1d6 100644 --- a/psutil/_psutil_linux.c +++ b/psutil/_psutil_linux.c @@ -23,7 +23,15 @@ #include #include #include -#include + +// see: https://github.com/giampaolo/psutil/issues/659 +#ifdef PSUTIL_ETHTOOL_MISSING_TYPES + #include + typedef __u64 u64; + typedef __u32 u32; + typedef __u16 u16; + typedef __u8 u8; +#endif #include #include "_psutil_linux.h" diff --git a/setup.py b/setup.py index 4c42548ef..73ecb4fea 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,10 @@ in Python. """ +import atexit import os import sys +import tempfile try: from setuptools import setup, Extension except ImportError: @@ -120,10 +122,29 @@ def get_winver(): ] # Linux elif sys.platform.startswith("linux"): + def get_ethtool_macro(): + # see: https://github.com/giampaolo/psutil/issues/659 + from distutils.unixccompiler import UnixCCompiler + from distutils.errors import CompileError + with tempfile.NamedTemporaryFile(suffix='.c', delete=False) as f: + f.write("#include ") + atexit.register(os.remove, f.name) + compiler = UnixCCompiler() + try: + compiler.compile([f.name]) + except CompileError: + return ("PSUTIL_ETHTOOL_MISSING_TYPES", 1) + else: + return None + + ETHTOOL_MACRO = get_ethtool_macro() + macros = [VERSION_MACRO] + if ETHTOOL_MACRO is not None: + macros.append(ETHTOOL_MACRO) extensions = [Extension( 'psutil._psutil_linux', sources=['psutil/_psutil_linux.c'], - define_macros=[VERSION_MACRO]), + define_macros=macros), posix_extension, ] # Solaris From aaea8bea6c7cb5cfce119e7896b71582af9cdf84 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 18:14:46 +0200 Subject: [PATCH 21/34] fix python 3 compilation error --- psutil/_psutil_linux.c | 3 +-- setup.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 5c63ef1d6..0a97d6679 100644 --- a/psutil/_psutil_linux.c +++ b/psutil/_psutil_linux.c @@ -637,13 +637,13 @@ PyMODINIT_FUNC PyInit__psutil_linux(void) void init_psutil_linux(void) #endif { + PyObject *v; #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, "RLIMIT_AS", RLIMIT_AS); @@ -658,7 +658,6 @@ void init_psutil_linux(void) PyModule_AddIntConstant(module, "RLIMIT_RSS", RLIMIT_RSS); PyModule_AddIntConstant(module, "RLIMIT_STACK", RLIMIT_STACK); - PyObject *v; #if defined(HAVE_LONG_LONG) if (sizeof(RLIM_INFINITY) > sizeof(long)) { v = PyLong_FromLongLong((PY_LONG_LONG) RLIM_INFINITY); diff --git a/setup.py b/setup.py index 73ecb4fea..1e3f7a541 100644 --- a/setup.py +++ b/setup.py @@ -126,7 +126,8 @@ def get_ethtool_macro(): # see: https://github.com/giampaolo/psutil/issues/659 from distutils.unixccompiler import UnixCCompiler from distutils.errors import CompileError - with tempfile.NamedTemporaryFile(suffix='.c', delete=False) as f: + with tempfile.NamedTemporaryFile( + suffix='.c', delete=False, mode="wt") as f: f.write("#include ") atexit.register(os.remove, f.name) compiler = UnixCCompiler() From e229dbe546cb87e9aa4579fa83b79065ef8ab26f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 19:19:16 +0200 Subject: [PATCH 22/34] #675: try to fix encoding issue --- psutil/_pslinux.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 573944fc7..0f96b6d39 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -471,7 +471,8 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): if file.endswith('6') and not os.path.exists(file): # IPv6 not supported return - with open(file, 'rt') as f: + kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() + with open(file, "rt", **kw) as f: f.readline() # skip the first line for line in f: try: @@ -504,7 +505,8 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): def process_unix(self, file, family, inodes, filter_pid=None): """Parse /proc/net/unix files.""" - with open(file, 'rt') as f: + kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() + with open(file, "rt", **kw) as f: f.readline() # skip the first line for line in f: tokens = line.split() From 77119ebe737b2ab562e20aa4980faba4d7624fbb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 30 Aug 2015 19:38:04 +0200 Subject: [PATCH 23/34] open files by using sys.getfilesystemencoding() + refactor stuff --- psutil/_pslinux.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 0f96b6d39..ed030cb31 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -56,7 +56,8 @@ CLOCK_TICKS = os.sysconf("SC_CLK_TCK") PAGESIZE = os.sysconf("SC_PAGE_SIZE") BOOT_TIME = None # set later -DEFAULT_ENCODING = sys.getdefaultencoding() +if PY3: + FS_ENCODING = sys.getfilesystemencoding() if enum is None: AF_LINK = socket.AF_PACKET else: @@ -115,6 +116,16 @@ class IOPriority(enum.IntEnum): TimeoutExpired = None +# --- utils + +def open_text(fname): + """On Python 3 opens a file in text mode by using fs encoding. + On Python 2 this is just an alias for open(name, 'rt'). + """ + kw = dict(encoding=FS_ENCODING) if PY3 else dict() + return open(fname, "rt", **kw) + + # --- named tuples def _get_cputimes_fields(): @@ -263,7 +274,7 @@ def cpu_count_logical(): # try to parse /proc/stat as a last resort if num == 0: search = re.compile('cpu\d') - with open('/proc/stat', 'rt') as f: + with open_text('/proc/stat') as f: for line in f: line = line.split(' ')[0] if search.match(line): @@ -471,8 +482,7 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): if file.endswith('6') and not os.path.exists(file): # IPv6 not supported return - kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() - with open(file, "rt", **kw) as f: + with open_text(file) as f: f.readline() # skip the first line for line in f: try: @@ -505,8 +515,7 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): def process_unix(self, file, family, inodes, filter_pid=None): """Parse /proc/net/unix files.""" - kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() - with open(file, "rt", **kw) as f: + with open_text(file) as f: f.readline() # skip the first line for line in f: tokens = line.split() @@ -577,7 +586,7 @@ def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ - with open("/proc/net/dev", "rt") as f: + with open_text("/proc/net/dev") as f: lines = f.readlines() retdict = {} for line in lines[2:]: @@ -628,7 +637,7 @@ def disk_io_counters(): # determine partitions we want to look for partitions = [] - with open("/proc/partitions", "rt") as f: + with open_text("/proc/partitions") as f: lines = f.readlines()[2:] for line in reversed(lines): _, _, _, name = line.split() @@ -645,7 +654,7 @@ def disk_io_counters(): partitions.append(name) # retdict = {} - with open("/proc/diskstats", "rt") as f: + with open_text("/proc/diskstats") as f: lines = f.readlines() for line in lines: # http://www.mjmwired.net/kernel/Documentation/iostats.txt @@ -671,7 +680,7 @@ def disk_io_counters(): def disk_partitions(all=False): """Return mounted disk partitions as a list of namedtuples""" fstypes = set() - with open("/proc/filesystems", "r") as f: + with open_text("/proc/filesystems") as f: for line in f: line = line.strip() if not line.startswith("nodev"): @@ -750,9 +759,7 @@ def __init__(self, pid): @wrap_exceptions def name(self): - fname = "/proc/%s/stat" % self.pid - kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() - with open(fname, "rt", **kw) as f: + with open_text("/proc/%s/stat" % self.pid) as f: data = f.read() # XXX - gets changed later and probably needs refactoring return data[data.find('(') + 1:data.rfind(')')] @@ -788,9 +795,7 @@ def exe(self): @wrap_exceptions def cmdline(self): - fname = "/proc/%s/cmdline" % self.pid - kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() - with open(fname, "rt", **kw) as f: + with open_text("/proc/%s/cmdline" % self.pid) as f: data = f.read() if data.endswith('\x00'): data = data[:-1] @@ -900,7 +905,7 @@ def memory_maps(self): Fields are explained in 'man proc'; here is an updated (Apr 2012) version: http://goo.gl/fmebo """ - with open("/proc/%s/smaps" % self.pid, "rt") as f: + with open_text("/proc/%s/smaps" % self.pid) as f: first_line = f.readline() current_block = [first_line] @@ -1023,7 +1028,7 @@ def threads(self): @wrap_exceptions def nice_get(self): - # with open('/proc/%s/stat' % self.pid, 'r') as f: + # with open_text('/proc/%s/stat' % self.pid) as f: # data = f.read() # return int(data.split()[18]) From 4afbb27273d7485550bbafd1f1b129a4b0632a73 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 12:23:47 +0200 Subject: [PATCH 24/34] try to fix appveyor failures --- HISTORY.rst | 18 ++++++++++-------- test/_windows.py | 2 +- test/test_psutil.py | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 5c00f9bf0..a1f6b224d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,19 +12,21 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #655: [Windows] different issues regarding unicode handling were fixed. On Python 2 all APIs returning a string will now return an encoded version of it by using sys.getfilesystemencoding() codec. The APIs involved are: - - psutil.net_if_addrs - - psutil.net_if_stats - - psutil.net_io_counters - - psutil.Process.cmdline - - psutil.Process.name - - psutil.Process.username - - psutil.users + - psutil.net_if_addrs() + - psutil.net_if_stats() + - psutil.net_io_counters() + - psutil.Process.cmdline() + - psutil.Process.name() + - psutil.Process.username() + - psutil.users() **Bug fixes** - #513: [Linux] fixed integer overflow for RLIM_INFINITY. - #641: [Windows] fixed many compilation warnings. (patch by Jeff Tang) -- #655: [Windows] net_if_stats UnicodeDecodeError in case of non-ASCII NIC +- #652: [Windows] net_if_addrs() UnicodeDecodeError in case of non-ASCII NIC + names. +- #655: [Windows] net_if_stats() UnicodeDecodeError in case of non-ASCII NIC names. - #659: [Linux] compilation error on Suse 10. (patch by maozguttman) - #664: [Linux] compilation error on Alpine Linux. (patch by Bart van Kleef) diff --git a/test/_windows.py b/test/_windows.py index 6c7b54468..a44cb7b02 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -479,8 +479,8 @@ def setUpClass(cls): cls.uexe = os.path.join(tdir, "psutil-è.exe") def setUp(self): - safe_remove(self.uexe) reap_children() + safe_remove(self.uexe) tearDown = setUp diff --git a/test/test_psutil.py b/test/test_psutil.py index 3b480caaf..b51627cfe 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -405,9 +405,9 @@ def safe_remove(file): os.remove(file) except OSError as err: if err.errno != errno.ENOENT: - # file is being used by another process - if WINDOWS and isinstance(err, WindowsError) and err.errno == 13: - return + # # file is being used by another process + # if WINDOWS and isinstance(err, WindowsError) and err.errno == 13: + # return raise From 83cd32591febc41a3d8057c17bbf0cc80513c460 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 12:31:39 +0200 Subject: [PATCH 25/34] test refactoring+ --- test/_windows.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/_windows.py b/test/_windows.py index a44cb7b02..6aa0c5038 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -477,15 +477,18 @@ def setUpClass(cls): with tempfile.NamedTemporaryFile() as f: tdir = os.path.dirname(f.name) cls.uexe = os.path.join(tdir, "psutil-è.exe") + shutil.copyfile(sys.executable, cls.uexe) + + @classmethod + def tearDownClass(cls): + safe_remove(cls.uexe) def setUp(self): reap_children() - safe_remove(self.uexe) tearDown = setUp def test_proc_exe(self): - shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) self.assertIsInstance(p.name(), str) @@ -493,14 +496,12 @@ def test_proc_exe(self): def test_proc_name(self): from psutil._pswindows import py2_strencode - shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) self.assertEqual( py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)), "psutil-è.exe") def test_proc_cmdline(self): - shutil.copyfile(sys.executable, self.uexe) subp = get_test_subprocess(cmd=[self.uexe]) p = psutil.Process(subp.pid) self.assertIsInstance("".join(p.cmdline()), str) @@ -517,7 +518,7 @@ def test_proc_cwd(self): def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) - with open(self.uexe, 'w'): + with open(self.uexe, 'rb'): new = set(p.open_files()) path = (new - start).pop().path self.assertIsInstance(path, str) From 3601942df655a1b1462d2e141b0cacb07d37d91a Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 12:40:44 +0200 Subject: [PATCH 26/34] try to fix appveyor failure --- HISTORY.rst | 1 + test/_windows.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index a1f6b224d..aefb74949 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -34,6 +34,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues (patch by sk6249) - #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by Steven Winfield) +- #675: [Linux] UnicodeDecodeError when listing UNIX sockets. 3.1.1 - 2015-07-15 diff --git a/test/_windows.py b/test/_windows.py index 6aa0c5038..ee30f700a 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -519,6 +519,8 @@ def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) with open(self.uexe, 'rb'): + if APPVEYOR: + time.sleep(0.2) new = set(p.open_files()) path = (new - start).pop().path self.assertIsInstance(path, str) From bafd7f4f6c189c9a35c72386839f8c2fd72c5fd6 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 13:44:02 +0200 Subject: [PATCH 27/34] skip failing test on appveyor --- HISTORY.rst | 1 - test/_windows.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index aefb74949..a1f6b224d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -34,7 +34,6 @@ Bug tracker at https://github.com/giampaolo/psutil/issues (patch by sk6249) - #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by Steven Winfield) -- #675: [Linux] UnicodeDecodeError when listing UNIX sockets. 3.1.1 - 2015-07-15 diff --git a/test/_windows.py b/test/_windows.py index ee30f700a..1663158b3 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -515,12 +515,11 @@ def test_proc_cwd(self): self.assertIsInstance(p.cwd(), str) self.assertEqual(p.cwd(), tdir) + @unittest.skipIf(APPVEYOR, "") def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) with open(self.uexe, 'rb'): - if APPVEYOR: - time.sleep(0.2) new = set(p.open_files()) path = (new - start).pop().path self.assertIsInstance(path, str) From d905793f6c8382e8070dc57624ffdf83a7274991 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 13:51:47 +0200 Subject: [PATCH 28/34] move unicode tests so that they are executed on all platforms --- test/_windows.py | 61 ----------------------------------------- test/test_psutil.py | 66 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/test/_windows.py b/test/_windows.py index 1663158b3..827cc4493 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -10,17 +10,14 @@ import errno import os import platform -import shutil import signal import subprocess import sys -import tempfile import time import traceback from test_psutil import APPVEYOR, WINDOWS from test_psutil import get_test_subprocess, reap_children, unittest -from test_psutil import safe_remove, safe_rmdir, chdir import mock try: @@ -469,68 +466,10 @@ def test_zombies(self): self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) -class TestUnicode(unittest.TestCase): - # See: https://github.com/giampaolo/psutil/issues/655 - - @classmethod - def setUpClass(cls): - with tempfile.NamedTemporaryFile() as f: - tdir = os.path.dirname(f.name) - cls.uexe = os.path.join(tdir, "psutil-è.exe") - shutil.copyfile(sys.executable, cls.uexe) - - @classmethod - def tearDownClass(cls): - safe_remove(cls.uexe) - - def setUp(self): - reap_children() - - tearDown = setUp - - def test_proc_exe(self): - subp = get_test_subprocess(cmd=[self.uexe]) - p = psutil.Process(subp.pid) - self.assertIsInstance(p.name(), str) - self.assertEqual(os.path.basename(p.name()), "psutil-è.exe") - - def test_proc_name(self): - from psutil._pswindows import py2_strencode - subp = get_test_subprocess(cmd=[self.uexe]) - self.assertEqual( - py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)), - "psutil-è.exe") - - def test_proc_cmdline(self): - subp = get_test_subprocess(cmd=[self.uexe]) - p = psutil.Process(subp.pid) - self.assertIsInstance("".join(p.cmdline()), str) - self.assertEqual(p.cmdline(), [self.uexe]) - - def test_proc_cwd(self): - tdir = tempfile.mkdtemp(prefix="psutil-è-") - self.addCleanup(safe_rmdir, tdir) - with chdir(tdir): - p = psutil.Process() - self.assertIsInstance(p.cwd(), str) - self.assertEqual(p.cwd(), tdir) - - @unittest.skipIf(APPVEYOR, "") - def test_proc_open_files(self): - p = psutil.Process() - start = set(p.open_files()) - with open(self.uexe, 'rb'): - new = set(p.open_files()) - path = (new - start).pop().path - self.assertIsInstance(path, str) - self.assertEqual(path.lower(), self.uexe.lower()) - - def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) - test_suite.addTest(unittest.makeSuite(TestUnicode)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() diff --git a/test/test_psutil.py b/test/test_psutil.py index b51627cfe..6b3bb0293 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -3083,6 +3083,68 @@ def test_pidof(self): self.assertIn(str(os.getpid()), output) +class TestUnicode(unittest.TestCase): + # See: https://github.com/giampaolo/psutil/issues/655 + + @classmethod + def setUpClass(cls): + with tempfile.NamedTemporaryFile() as f: + tdir = os.path.dirname(f.name) + cls.uexe = os.path.join(tdir, "psutil-è.exe") + shutil.copyfile(sys.executable, cls.uexe) + if POSIX: + st = os.stat(cls.uexe) + os.chmod(cls.uexe, st.st_mode | stat.S_IEXEC) + + @classmethod + def tearDownClass(cls): + safe_remove(cls.uexe) + + def setUp(self): + reap_children() + + tearDown = setUp + + def test_proc_exe(self): + subp = get_test_subprocess(cmd=[self.uexe]) + p = psutil.Process(subp.pid) + self.assertIsInstance(p.name(), str) + self.assertEqual(os.path.basename(p.name()), "psutil-è.exe") + + def test_proc_name(self): + subp = get_test_subprocess(cmd=[self.uexe]) + if WINDOWS: + from psutil._pswindows import py2_strencode + name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)) + else: + name = psutil.Process(subp.pid).name() + self.assertEqual(name, "psutil-è.exe") + + def test_proc_cmdline(self): + subp = get_test_subprocess(cmd=[self.uexe]) + p = psutil.Process(subp.pid) + self.assertIsInstance("".join(p.cmdline()), str) + self.assertEqual(p.cmdline(), [self.uexe]) + + def test_proc_cwd(self): + tdir = tempfile.mkdtemp(prefix="psutil-è-") + self.addCleanup(safe_rmdir, tdir) + with chdir(tdir): + p = psutil.Process() + self.assertIsInstance(p.cwd(), str) + self.assertEqual(p.cwd(), tdir) + + @unittest.skipIf(APPVEYOR, "") + def test_proc_open_files(self): + p = psutil.Process() + start = set(p.open_files()) + with open(self.uexe, 'rb'): + new = set(p.open_files()) + path = (new - start).pop().path + self.assertIsInstance(path, str) + self.assertEqual(path.lower(), self.uexe.lower()) + + def main(): tests = [] test_suite = unittest.TestSuite() @@ -3092,6 +3154,7 @@ def main(): tests.append(TestMisc) tests.append(TestExampleScripts) tests.append(LimitedUserTestCase) + tests.append(TestUnicode) if POSIX: from _posix import PosixSpecificTestCase @@ -3103,9 +3166,8 @@ def main(): from _linux import LinuxSpecificTestCase as stc elif WINDOWS: from _windows import WindowsSpecificTestCase as stc - from _windows import TestDualProcessImplementation, TestUnicode + from _windows import TestDualProcessImplementation tests.append(TestDualProcessImplementation) - tests.append(TestUnicode) elif OSX: from _osx import OSXSpecificTestCase as stc elif BSD: From 98befb0bc43751219ccd5489eb37ae2aedb7266e Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 14:12:03 +0200 Subject: [PATCH 29/34] #675: [Linux] net_connections(); UnicodeDecodeError may occur when listing UNIX sockets --- HISTORY.rst | 2 ++ psutil/_pslinux.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index a1f6b224d..2ad1697af 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -34,6 +34,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues (patch by sk6249) - #672: [Windows] compilation fails if using Windows SDK v8.0. (patch by Steven Winfield) +- #675: [Linux] net_connections(); UnicodeDecodeError may occur when listing + UNIX sockets. 3.1.1 - 2015-07-15 diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index ed030cb31..8356cf952 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -515,7 +515,9 @@ def process_inet(self, file, family, type_, inodes, filter_pid=None): def process_unix(self, file, family, inodes, filter_pid=None): """Parse /proc/net/unix files.""" - with open_text(file) as f: + # see: https://github.com/giampaolo/psutil/issues/675 + kw = dict(encoding=FS_ENCODING, errors='replace') if PY3 else dict() + with open(file, 'rt', **kw) as f: f.readline() # skip the first line for line in f: tokens = line.split() From 8d66475bbeaa04aa6c4064ceadf0f4f176f5e03d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 31 Aug 2015 18:41:24 +0200 Subject: [PATCH 30/34] linux / setup.py: do not print warnings --- setup.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1e3f7a541..e91dd58cb 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,8 @@ """ import atexit +import contextlib +import io import os import sys import tempfile @@ -42,6 +44,16 @@ def get_description(): return f.read() +@contextlib.contextmanager +def captured_output(stream_name): + orig = getattr(sys, stream_name) + setattr(sys, stream_name, io.StringIO()) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig) + + VERSION = get_version() VERSION_MACRO = ('PSUTIL_VERSION', int(VERSION.replace('.', ''))) @@ -132,7 +144,9 @@ def get_ethtool_macro(): atexit.register(os.remove, f.name) compiler = UnixCCompiler() try: - compiler.compile([f.name]) + with captured_output('stderr'): + with captured_output('stdout'): + compiler.compile([f.name]) except CompileError: return ("PSUTIL_ETHTOOL_MISSING_TYPES", 1) else: From e0d3f5a654e83db58b04f189774eeb3a96cd8bd7 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 14 Jun 2015 13:59:44 +0200 Subject: [PATCH 31/34] fix OSX test failure --- Makefile | 7 ++++--- test/test_psutil.py | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index eaace20b4..30597d7d0 100644 --- a/Makefile +++ b/Makefile @@ -35,9 +35,10 @@ build: clean # useful deps which are nice to have while developing / testing setup-dev-env: install-git-hooks - python -c "import urllib2; \ - r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \ - open('/tmp/get-pip.py', 'w').write(r.read());" + python -c "import urllib2, ssl; \ + context = ssl._create_unverified_context(); \ + r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py', context=context); \ + 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 diff --git a/test/test_psutil.py b/test/test_psutil.py index 6b3bb0293..3cf401218 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -3090,7 +3090,7 @@ class TestUnicode(unittest.TestCase): def setUpClass(cls): with tempfile.NamedTemporaryFile() as f: tdir = os.path.dirname(f.name) - cls.uexe = os.path.join(tdir, "psutil-è.exe") + cls.uexe = os.path.realpath(os.path.join(tdir, "psutil-è.exe")) shutil.copyfile(sys.executable, cls.uexe) if POSIX: st = os.stat(cls.uexe) @@ -3127,7 +3127,7 @@ def test_proc_cmdline(self): self.assertEqual(p.cmdline(), [self.uexe]) def test_proc_cwd(self): - tdir = tempfile.mkdtemp(prefix="psutil-è-") + tdir = os.path.realpath(tempfile.mkdtemp(prefix="psutil-è-")) self.addCleanup(safe_rmdir, tdir) with chdir(tdir): p = psutil.Process() @@ -3142,7 +3142,10 @@ def test_proc_open_files(self): new = set(p.open_files()) path = (new - start).pop().path self.assertIsInstance(path, str) - self.assertEqual(path.lower(), self.uexe.lower()) + if WINDOWS: + self.assertEqual(path.lower(), self.uexe.lower()) + else: + self.assertEqual(path, self.uexe) def main(): From 650c839f8e270f25e17eb44c2aa4127570a2e6ca Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 2 Sep 2015 12:37:57 +0200 Subject: [PATCH 32/34] refactor setup.py + fix Makefile --- Makefile | 9 +++++---- setup.py | 45 +++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index 30597d7d0..153a07733 100644 --- a/Makefile +++ b/Makefile @@ -35,10 +35,11 @@ build: clean # useful deps which are nice to have while developing / testing setup-dev-env: install-git-hooks - python -c "import urllib2, ssl; \ - context = ssl._create_unverified_context(); \ - r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py', context=context); \ - open('/tmp/get-pip.py', 'w').write(r.read());" + python -c "import urllib2, ssl; \ + context = ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None; \ + kw = dict(context=context) if context else {}; \ + r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py', **kw); \ + 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 diff --git a/setup.py b/setup.py index e91dd58cb..8922f0fcf 100644 --- a/setup.py +++ b/setup.py @@ -63,12 +63,10 @@ def captured_output(stream_name): libraries = [] if sys.platform.startswith("sunos"): libraries.append('socket') - posix_extension = Extension( 'psutil._psutil_posix', sources=['psutil/_psutil_posix.c'], - libraries=libraries, - ) + libraries=libraries) # Windows if sys.platform.startswith("win32"): @@ -76,7 +74,7 @@ def get_winver(): maj, min = sys.getwindowsversion()[0:2] return '0x0%s' % ((maj * 100) + min) - extensions = [Extension( + ext = Extension( 'psutil._psutil_windows', sources=[ 'psutil/_psutil_windows.c', @@ -97,15 +95,16 @@ def get_winver(): ('PSAPI_VERSION', 1), ], libraries=[ - "psapi", "kernel32", "advapi32", "shell32", "netapi32", "iphlpapi", - "wtsapi32", "ws2_32", + "psapi", "kernel32", "advapi32", "shell32", "netapi32", + "iphlpapi", "wtsapi32", "ws2_32", ], # extra_compile_args=["/Z7"], # extra_link_args=["/DEBUG"] - )] + ) + extensions = [ext] # OS X elif sys.platform.startswith("darwin"): - extensions = [Extension( + ext = Extension( 'psutil._psutil_osx', sources=[ 'psutil/_psutil_osx.c', @@ -115,23 +114,19 @@ def get_winver(): define_macros=[VERSION_MACRO], extra_link_args=[ '-framework', 'CoreFoundation', '-framework', 'IOKit' - ], - ), - posix_extension, - ] + ]) + extensions = [ext, posix_extension] # FreeBSD elif sys.platform.startswith("freebsd"): - extensions = [Extension( + ext = Extension( 'psutil._psutil_bsd', sources=[ 'psutil/_psutil_bsd.c', 'psutil/_psutil_common.c', - 'psutil/arch/bsd/process_info.c' - ], + 'psutil/arch/bsd/process_info.c'], define_macros=[VERSION_MACRO], - libraries=["devstat"]), - posix_extension, - ] + libraries=["devstat"]) + extensions = [ext, posix_extension] # Linux elif sys.platform.startswith("linux"): def get_ethtool_macro(): @@ -156,21 +151,19 @@ def get_ethtool_macro(): macros = [VERSION_MACRO] if ETHTOOL_MACRO is not None: macros.append(ETHTOOL_MACRO) - extensions = [Extension( + ext = Extension( 'psutil._psutil_linux', sources=['psutil/_psutil_linux.c'], - define_macros=macros), - posix_extension, - ] + define_macros=macros) + extensions = [ext, posix_extension] # Solaris elif sys.platform.lower().startswith('sunos'): - extensions = [Extension( + ext = Extension( 'psutil._psutil_sunos', sources=['psutil/_psutil_sunos.c'], define_macros=[VERSION_MACRO], - libraries=['kstat', 'nsl', 'socket']), - posix_extension, - ] + libraries=['kstat', 'nsl', 'socket']) + extensions = [ext, posix_extension] else: sys.exit('platform %s is not supported' % sys.platform) From d91b07921af1578a2c73667917baa993beae4bed Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 2 Sep 2015 12:44:14 +0200 Subject: [PATCH 33/34] try to fix appveyor failure --- INSTALL.rst | 8 ++++---- test/test_psutil.py | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/INSTALL.rst b/INSTALL.rst index e518c430e..fdbc4d6dd 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -30,11 +30,11 @@ Compiling on Windows using Visual Studio ======================================== 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:: +no longer supported). You must have the same version of Visual Studio used to +compile your installation of Python, that is:: -* Python 2.6: VS 2008 -* Python 2.7: VS 2008 +* Python 2.6: VS 2008 (download it from `here `_) +* Python 2.7: VS 2008 (download it from `here `_) * Python 3.3, 3.4: VS 2010 (you can download it from `MS website `_) * Python 3.5: `VS 2015 UP `_ diff --git a/test/test_psutil.py b/test/test_psutil.py index 3cf401218..cf380eab4 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -3098,7 +3098,8 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - safe_remove(cls.uexe) + if not APPVEYOR: + safe_remove(cls.uexe) def setUp(self): reap_children() From 6a5f980a23ecda2b7bbe9ea95bc165df6a6951ad Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 2 Sep 2015 13:59:08 +0200 Subject: [PATCH 34/34] pre-release updates --- HISTORY.rst | 2 +- README.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 2ad1697af..0f7d075e4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,6 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -3.2.0 - XXXX-XX-XX +3.2.0 - 2015-09-02 ================== **Enhancements** diff --git a/README.rst b/README.rst index 3979924d1..a5549e137 100644 --- a/README.rst +++ b/README.rst @@ -350,6 +350,7 @@ http://groups.google.com/group/psutil/ Timeline ======== +- 2015-09-02: `psutil-3.1.1.tar.gz `_ - 2015-07-15: `psutil-3.1.1.tar.gz `_ - 2015-07-15: `psutil-3.1.0.tar.gz `_ - 2015-06-18: `psutil-3.0.1.tar.gz `_