summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-02-11 23:06:43 +0100
committerGiampaolo Rodola <g.rodola@gmail.com>2020-02-11 23:06:43 +0100
commit579921dd7fb7de09295e5e8f4e5d02ba0e359c23 (patch)
treecb46fc9c1d42d7681a79be303292ee47b33d40b9
parentacfacb47e57df79f0177e5ec3ae79c37d7dea8e3 (diff)
parent9e2ca978b211993066b0dc41da9aa63429655406 (diff)
downloadpsutil-579921dd7fb7de09295e5e8f4e5d02ba0e359c23.tar.gz
Merge branch 'master' of github.com:giampaolo/psutil
-rw-r--r--HISTORY.rst2
-rw-r--r--psutil/_common.py8
-rw-r--r--psutil/_psutil_common.c48
-rw-r--r--psutil/_psutil_common.h16
-rw-r--r--psutil/_psutil_windows.c22
-rw-r--r--psutil/_pswindows.py16
-rw-r--r--psutil/arch/windows/cpu.c12
-rw-r--r--psutil/arch/windows/wmi.c2
-rw-r--r--psutil/tests/__init__.py6
-rwxr-xr-xpsutil/tests/runner.py1
-rwxr-xr-xpsutil/tests/test_system.py7
-rw-r--r--psutil/tests/test_unicode.py5
-rwxr-xr-xpsutil/tests/test_windows.py25
-rwxr-xr-xscripts/internal/winmake.py12
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: