diff options
author | Giampaolo Rodola <g.rodola@gmail.com> | 2020-02-11 23:06:43 +0100 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2020-02-11 23:06:43 +0100 |
commit | 579921dd7fb7de09295e5e8f4e5d02ba0e359c23 (patch) | |
tree | cb46fc9c1d42d7681a79be303292ee47b33d40b9 | |
parent | acfacb47e57df79f0177e5ec3ae79c37d7dea8e3 (diff) | |
parent | 9e2ca978b211993066b0dc41da9aa63429655406 (diff) | |
download | psutil-579921dd7fb7de09295e5e8f4e5d02ba0e359c23.tar.gz |
Merge branch 'master' of github.com:giampaolo/psutil
-rw-r--r-- | HISTORY.rst | 2 | ||||
-rw-r--r-- | psutil/_common.py | 8 | ||||
-rw-r--r-- | psutil/_psutil_common.c | 48 | ||||
-rw-r--r-- | psutil/_psutil_common.h | 16 | ||||
-rw-r--r-- | psutil/_psutil_windows.c | 22 | ||||
-rw-r--r-- | psutil/_pswindows.py | 16 | ||||
-rw-r--r-- | psutil/arch/windows/cpu.c | 12 | ||||
-rw-r--r-- | psutil/arch/windows/wmi.c | 2 | ||||
-rw-r--r-- | psutil/tests/__init__.py | 6 | ||||
-rwxr-xr-x | psutil/tests/runner.py | 1 | ||||
-rwxr-xr-x | psutil/tests/test_system.py | 7 | ||||
-rw-r--r-- | psutil/tests/test_unicode.py | 5 | ||||
-rwxr-xr-x | psutil/tests/test_windows.py | 25 | ||||
-rwxr-xr-x | scripts/internal/winmake.py | 12 |
14 files changed, 141 insertions, 41 deletions
diff --git a/HISTORY.rst b/HISTORY.rst index 68309241..233236cc 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -18,7 +18,7 @@ XXXX-XX-XX raising AccessDenied). - 1679_: [Windows] net_connections() and Process.connections() are 10% faster. - 1681_: [Linux] disk_partitions() now also shows swap partitions. - +- 1686_: [Windows] added support for PyPy on Windows. **Bug fixes** diff --git a/psutil/_common.py b/psutil/_common.py index 453c771d..9306cd15 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -790,8 +790,14 @@ def hilite(s, ok=True, bold=False): if bool(os.getenv('PSUTIL_DEBUG', 0)): + import inspect + def debug(msg): - print("psutil-debug> " + msg, file=sys.stderr) + """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" + fname, lineno, func_name, lines, index = inspect.getframeinfo( + inspect.currentframe().f_back) + print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), + file=sys.stderr) else: def debug(msg): pass diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index 990d59a6..9bfaf92c 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -20,7 +20,47 @@ int PSUTIL_TESTING = 0; // ==================================================================== -// --- Python functions and backward compatibility +// --- Backward compatibility with missing Python.h APIs +// ==================================================================== + +// PyPy on Windows +#if defined(PYPY_VERSION) && !defined(PyErr_SetFromWindowsErrWithFilename) +PyObject * +PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) { + PyObject *py_exc = NULL; + PyObject *py_winerr = NULL; + + if (winerr == 0) + winerr = GetLastError(); + if (filename == NULL) { + py_exc = PyObject_CallFunction(PyExc_OSError, "(is)", winerr, + strerror(winerr)); + } + else { + py_exc = PyObject_CallFunction(PyExc_OSError, "(iss)", winerr, + strerror(winerr), filename); + } + if (py_exc == NULL) + return NULL; + + py_winerr = Py_BuildValue("i", winerr); + if (py_winerr == NULL) + goto error; + if (PyObject_SetAttrString(py_exc, "winerror", py_winerr) != 0) + goto error; + PyErr_SetObject(PyExc_OSError, py_exc); + Py_XDECREF(py_exc); + return NULL; + +error: + Py_XDECREF(py_exc); + Py_XDECREF(py_winerr); + return NULL; +} +#endif + +// ==================================================================== +// --- Custom exceptions // ==================================================================== /* @@ -44,11 +84,6 @@ PyErr_SetFromOSErrnoWithSyscall(const char *syscall) { return NULL; } - -// ==================================================================== -// --- Custom exceptions -// ==================================================================== - /* * Set OSError(errno=ESRCH, strerror="No such process (originated from") * Python exception. @@ -133,6 +168,7 @@ psutil_setup(void) { // --- Windows // ==================================================================== + #ifdef PSUTIL_WINDOWS #include <windows.h> diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h index 2fccab81..2886d611 100644 --- a/psutil/_psutil_common.h +++ b/psutil/_psutil_common.h @@ -16,7 +16,7 @@ extern int PSUTIL_DEBUG; static const int PSUTIL_CONN_NONE = 128; // ==================================================================== -// --- Python functions and backward compatibility +// --- Backward compatibility with missing Python.h APIs // ==================================================================== #if PY_MAJOR_VERSION < 3 @@ -60,9 +60,14 @@ static const int PSUTIL_CONN_NONE = 128; #endif #endif -#if PY_MAJOR_VERSION < 3 +// Python 2 or PyPy on Windows +#ifndef PyLong_FromPid #if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG)) - #define PyLong_FromPid PyInt_FromLong + #if PY_MAJOR_VERSION >= 3 + #define PyLong_FromPid PyLong_FromLong + #else + #define PyLong_FromPid PyInt_FromLong + #endif #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG #define PyLong_FromPid PyLong_FromLongLong #else @@ -122,4 +127,9 @@ int psutil_setup(void); PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname); PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname); PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall); + + #if defined(PYPY_VERSION) && !defined(PyErr_SetFromWindowsErrWithFilename) + PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, + const char *filename); + #endif #endif diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index e63cf97f..f9405707 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -228,8 +228,10 @@ psutil_proc_wait(PyObject *self, PyObject *args) { // return None instead. Py_RETURN_NONE; } - else - return PyErr_SetFromWindowsErr(0); + else { + PyErr_SetFromWindowsErr(0); + return NULL; + } } // wait until the process has terminated @@ -281,7 +283,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_cpu_times(PyObject *self, PyObject *args) { - pid_t pid; + DWORD pid; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; @@ -332,7 +334,7 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) { */ static PyObject * psutil_proc_create_time(PyObject *self, PyObject *args) { - pid_t pid; + DWORD pid; long long unix_time; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; @@ -698,8 +700,10 @@ psutil_virtual_mem(PyObject *self, PyObject *args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); - if (! GlobalMemoryStatusEx(&memInfo)) - return PyErr_SetFromWindowsErr(0); + if (! GlobalMemoryStatusEx(&memInfo)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } return Py_BuildValue("(LLLLLL)", memInfo.ullTotalPhys, // total memInfo.ullAvailPhys, // avail @@ -1571,8 +1575,10 @@ static PyObject * psutil_sensors_battery(PyObject *self, PyObject *args) { SYSTEM_POWER_STATUS sps; - if (GetSystemPowerStatus(&sps) == 0) - return PyErr_SetFromWindowsErr(0); + if (GetSystemPowerStatus(&sps) == 0) { + PyErr_SetFromWindowsErr(0); + return NULL; + } return Py_BuildValue( "iiiI", sps.ACLineStatus, // whether AC is connected: 0=no, 1=yes, 255=unknown diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index c9517b91..d8abf2e6 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -17,6 +17,7 @@ from . import _common from ._common import AccessDenied from ._common import conn_tmap from ._common import conn_to_ntuple +from ._common import debug from ._common import ENCODING from ._common import ENCODING_ERRS from ._common import isfile_strict @@ -80,7 +81,7 @@ __extra__all__ = [ CONN_DELETE_TCB = "DELETE_TCB" ERROR_PARTIAL_COPY = 299 - +PYPY = '__pypy__' in sys.builtin_module_names if enum is None: AF_LINK = -1 @@ -752,7 +753,18 @@ class Process(object): @wrap_exceptions @memoize_when_activated def exe(self): - exe = cext.proc_exe(self.pid) + if PYPY: + try: + exe = cext.proc_exe(self.pid) + except WindowsError as err: + # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens + # (perhaps PyPy's JIT delaying garbage collection of files?). + if err.errno == 24: + debug("%r forced into AccessDenied" % err) + raise AccessDenied(self.pid, self._name) + raise + else: + exe = cext.proc_exe(self.pid) if not PY3: exe = py2_strencode(exe) if exe.startswith('\\'): diff --git a/psutil/arch/windows/cpu.c b/psutil/arch/windows/cpu.c index 9a22e149..18f32e59 100644 --- a/psutil/arch/windows/cpu.c +++ b/psutil/arch/windows/cpu.c @@ -50,8 +50,10 @@ psutil_cpu_times(PyObject *self, PyObject *args) { double idle, kernel, user, system; FILETIME idle_time, kernel_time, user_time; - if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) - return PyErr_SetFromWindowsErr(0); + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } idle = (double)((HI_T * idle_time.dwHighDateTime) + \ (LO_T * idle_time.dwLowDateTime)); @@ -384,8 +386,10 @@ psutil_cpu_freq(PyObject *self, PyObject *args) { // Allocate size. size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION); pBuffer = (BYTE*)LocalAlloc(LPTR, size); - if (! pBuffer) - return PyErr_SetFromWindowsErr(0); + if (! pBuffer) { + PyErr_SetFromWindowsErr(0); + return NULL; + } // Syscall. ret = CallNtPowerInformation( diff --git a/psutil/arch/windows/wmi.c b/psutil/arch/windows/wmi.c index b790c08e..42a70df7 100644 --- a/psutil/arch/windows/wmi.c +++ b/psutil/arch/windows/wmi.c @@ -96,7 +96,7 @@ psutil_init_loadavg_counter(PyObject *self, PyObject *args) { Py_RETURN_NONE; error: - PyErr_SetExcFromWindowsErr(PyExc_OSError, 0); + PyErr_SetFromWindowsErr(0); return NULL; } diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 8a373386..3e4dc880 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -118,7 +118,6 @@ TRAVIS = bool(os.environ.get('TRAVIS')) APPVEYOR = bool(os.environ.get('APPVEYOR')) CIRRUS = bool(os.environ.get('CIRRUS')) CI_TESTING = TRAVIS or APPVEYOR or CIRRUS -PYPY = '__pypy__' in sys.builtin_module_names # --- configurable defaults @@ -1111,9 +1110,12 @@ else: ext = ".dll" dst = tempfile.mktemp(prefix=dst_prefix, suffix=ext) libs = [x.path for x in psutil.Process().memory_maps() if - os.path.splitext(x.path)[1].lower() == ext and + x.path.lower().endswith(ext) and 'python' in os.path.basename(x.path).lower() and 'wow64' not in x.path.lower()] + if PYPY and not libs: + libs = [x.path for x in psutil.Process().memory_maps() if + 'pypy' in os.path.basename(x.path).lower()] src = random.choice(libs) shutil.copyfile(src, dst) cfile = None diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py index 1a28aa43..f8601bad 100755 --- a/psutil/tests/runner.py +++ b/psutil/tests/runner.py @@ -177,6 +177,7 @@ def save_failed_tests(result): with open(FAILED_TESTS_FNAME, 'wt') as f: for t in result.errors + result.failures: tname = str(t[0]) + unittest.defaultTestLoader.loadTestsFromName(tname) f.write(tname + '\n') diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index c6f8a17a..223f91a2 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -31,9 +31,9 @@ from psutil import SUNOS from psutil import WINDOWS from psutil._compat import FileNotFoundError from psutil._compat import long -from psutil.tests import CI_TESTING from psutil.tests import ASCII_FS from psutil.tests import check_net_address +from psutil.tests import CI_TESTING from psutil.tests import DEVNULL from psutil.tests import enum from psutil.tests import get_test_subprocess @@ -45,6 +45,7 @@ from psutil.tests import HAS_SENSORS_BATTERY from psutil.tests import HAS_SENSORS_FANS from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import mock +from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import retry_on_failure from psutil.tests import safe_rmpath @@ -121,6 +122,8 @@ class TestSystemAPIs(unittest.TestCase): else: self.fail("subprocess not found") + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs(self): def callback(p): pids.append(p.pid) @@ -176,6 +179,8 @@ class TestSystemAPIs(unittest.TestCase): for p in gone: self.assertTrue(hasattr(p, 'returncode')) + @unittest.skipIf(PYPY and WINDOWS, + "get_test_subprocess() unreliable on PYPY + WINDOWS") def test_wait_procs_no_timeout(self): sproc1 = get_test_subprocess() sproc2 = get_test_subprocess() diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index eecd7dc4..9e459fbe 100644 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -61,6 +61,7 @@ from psutil import BSD from psutil import MACOS from psutil import OPENBSD from psutil import POSIX +from psutil import WINDOWS from psutil._compat import PY3 from psutil._compat import u from psutil.tests import APPVEYOR @@ -194,6 +195,7 @@ class _BaseFSAPIsTests(object): if self.expect_exact_path_match(): self.assertEqual(cwd, dname) + @unittest.skipIf(PYPY and WINDOWS, "fails on PYPY + WINDOWS") def test_proc_open_files(self): p = psutil.Process() start = set(p.open_files()) @@ -261,6 +263,8 @@ class _BaseFSAPIsTests(object): @unittest.skipIf(not HAS_MEMORY_MAPS, "not supported") @unittest.skipIf(not PY3, "ctypes does not support unicode on PY2") + @unittest.skipIf(PYPY and WINDOWS, + "copyload_shared_lib() unsupported on PYPY + WINDOWS") def test_memory_maps(self): # XXX: on Python 2, using ctypes.CDLL with a unicode path # opens a message box which blocks the test run. @@ -323,6 +327,7 @@ class TestNonFSAPIS(unittest.TestCase): reap_children() @unittest.skipIf(not HAS_ENVIRON, "not supported") + @unittest.skipIf(PYPY and WINDOWS, "segfaults on PYPY + WINDOWS") def test_proc_environ(self): # Note: differently from others, this test does not deal # with fs paths. On Python 2 subprocess module is broken as diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 6d4e8599..8a93743f 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -27,12 +27,14 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import mock from psutil.tests import PY3 +from psutil.tests import PYPY from psutil.tests import reap_children from psutil.tests import retry_on_failure from psutil.tests import sh from psutil.tests import unittest -if WINDOWS: + +if WINDOWS and not PYPY: with warnings.catch_warnings(): warnings.simplefilter("ignore") import win32api # requires "pip install pypiwin32" @@ -61,13 +63,18 @@ def wrap_exceptions(fun): return wrapper +@unittest.skipIf(PYPY, "pywin32 not available on PYPY") # skip whole module +class TestCase(unittest.TestCase): + pass + + # =================================================================== # System APIs # =================================================================== @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestCpuAPIs(unittest.TestCase): +class TestCpuAPIs(TestCase): @unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ, 'NUMBER_OF_PROCESSORS env var is not available') @@ -106,7 +113,7 @@ class TestCpuAPIs(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSystemAPIs(unittest.TestCase): +class TestSystemAPIs(TestCase): def test_nic_names(self): out = sh('ipconfig /all') @@ -230,7 +237,7 @@ class TestSystemAPIs(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestSensorsBattery(unittest.TestCase): +class TestSensorsBattery(TestCase): def test_has_battery(self): if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: @@ -291,7 +298,7 @@ class TestSensorsBattery(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcess(unittest.TestCase): +class TestProcess(TestCase): @classmethod def setUpClass(cls): @@ -519,7 +526,7 @@ class TestProcess(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestProcessWMI(unittest.TestCase): +class TestProcessWMI(TestCase): """Compare Process API results with WMI.""" @classmethod @@ -585,7 +592,7 @@ class TestProcessWMI(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestDualProcessImplementation(unittest.TestCase): +class TestDualProcessImplementation(TestCase): """ Certain APIs on Windows have 2 internal implementations, one based on documented Windows APIs, another one based @@ -668,7 +675,7 @@ class TestDualProcessImplementation(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class RemoteProcessTestCase(unittest.TestCase): +class RemoteProcessTestCase(TestCase): """Certain functions require calling ReadProcessMemory. This trivially works when called on the current process. Check that this works on other processes, especially when they @@ -764,7 +771,7 @@ class RemoteProcessTestCase(unittest.TestCase): @unittest.skipIf(not WINDOWS, "WINDOWS only") -class TestServices(unittest.TestCase): +class TestServices(TestCase): def test_win_service_iter(self): valid_statuses = set([ diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index ac08c03f..fe0a73dc 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -36,6 +36,7 @@ GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" PY3 = sys.version_info[0] == 3 HERE = os.path.abspath(os.path.dirname(__file__)) ROOT_DIR = os.path.realpath(os.path.join(HERE, "..", "..")) +PYPY = '__pypy__' in sys.builtin_module_names DEPS = [ "coverage", "flake8", @@ -43,7 +44,6 @@ DEPS = [ "pdbpp", "pip", "pyperf", - "pypiwin32==219" if sys.version_info[:2] <= (3, 4) else "pypiwin32", "pyreadline", "setuptools", "wheel", @@ -56,6 +56,12 @@ if sys.version_info[:2] <= (2, 7): DEPS.append('mock') if sys.version_info[:2] <= (3, 2): DEPS.append('ipaddress') +if PYPY: + pass +elif sys.version_info[:2] <= (3, 4): + DEPS.append("pypiwin32==219") +else: + DEPS.append("pypiwin32") _cmds = {} if PY3: @@ -268,8 +274,8 @@ def upload_wheels(): def install_pip(): """Install pip""" try: - import pip # NOQA - except ImportError: + sh('%s -c "import pip"' % PYTHON) + except SystemExit: if PY3: from urllib.request import urlopen else: |