summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2019-04-05 02:16:40 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2019-04-05 02:16:40 +0200
commit84448be84278b1701ea1d4163cd46c6b959bf1be (patch)
treeae7f92f6e4e91c1218669e634abfb4ce5c5968de
parent16128f7298f490d11368dad316fa8e53348ed52d (diff)
parente471e7cbad9e2d84f9fb114da86df78755836852 (diff)
downloadpsutil-osx-ionice.tar.gz
merge from masterosx-ionice
-rw-r--r--HISTORY.rst8
-rw-r--r--Makefile5
-rw-r--r--docs/index.rst63
-rw-r--r--psutil/__init__.py4
-rw-r--r--psutil/_pslinux.py34
-rw-r--r--psutil/_psutil_common.c2
-rw-r--r--psutil/_psutil_windows.c71
-rw-r--r--psutil/_pswindows.py131
-rw-r--r--psutil/arch/windows/global.c33
-rw-r--r--psutil/arch/windows/global.h4
-rw-r--r--psutil/arch/windows/ntextapi.h4
-rw-r--r--psutil/arch/windows/process_handles.c11
-rw-r--r--psutil/arch/windows/process_info.c55
-rwxr-xr-xpsutil/tests/runner.py29
-rwxr-xr-xpsutil/tests/test_contracts.py7
-rwxr-xr-xpsutil/tests/test_process.py151
-rwxr-xr-xpsutil/tests/test_windows.py16
-rwxr-xr-xscripts/internal/winmake.py10
18 files changed, 394 insertions, 244 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index 79963855..1c745b8b 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -10,6 +10,10 @@
the number of physical CPUs in case /proc/cpuinfo does not provide this info.
- 1458_: provide coloured test output. Also show failures on KeyboardInterrupt.
- 1464_: various docfixes (always point to python3 doc, fix links, etc.).
+- 1473_: [Windows] process IO priority (ionice()) values are now exposed as 4
+ new constants: IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, IOPRIO_HIGH.
+ Also it was not possible to set high I/O priority (not it is).
+- 1478_: add make command to re-run tests failed on last run.
**Bug fixes**
@@ -21,6 +25,10 @@
exist. (patch by Cedric Lamoriniere)
- 1471_: [SunOS] Process name() and cmdline() can return SystemError. (patch
by Daniel Beer)
+- 1475_: [Windows] OSError.winerror attribute wasn't properly checked resuling
+ in WindowsError being raised instead of AccessDenied.
+- 1477_: [Windows] wrong or absent error handling for private NTSTATUS Windows
+ APIs. Different process methods were affected by this.
5.6.1
=====
diff --git a/Makefile b/Makefile
index 7d838e87..e91ae342 100644
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,7 @@ clean: ## Remove all build files.
*.egg-info \
*\$testfn* \
.coverage \
+ .failed-tests.txt \
.tox \
build/ \
dist/ \
@@ -151,6 +152,10 @@ test-by-name: ## e.g. make test-by-name ARGS=psutil.tests.test_system.TestSyste
${MAKE} install
@$(TEST_PREFIX) $(PYTHON) -m unittest -v $(ARGS)
+test-failed: ## Re-run tests which failed on last run
+ ${MAKE} install
+ $(TEST_PREFIX) $(PYTHON) -c "import psutil.tests.runner as r; r.run(last_failed=True)"
+
test-coverage: ## Run test coverage.
${MAKE} install
# Note: coverage options are controlled by .coveragerc file
diff --git a/docs/index.rst b/docs/index.rst
index cd9c6d7c..1fc13624 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1228,7 +1228,6 @@ Process class
>>> p.nice(psutil.HIGH_PRIORITY_CLASS)
-
.. method:: ionice(ioclass=None, value=None)
Get or set process I/O niceness (priority).
@@ -1239,35 +1238,33 @@ Process class
I/O priority even further.
Here's the possible platform-dependent *ioclass* values.
- Linux:
+ Linux (see `ioprio_get`_ manual):
- * ``IOPRIO_CLASS_RT``: (highest priority) the process gets first access
- to the disk every time. Use it with care as it can starve the entire
+ * ``IOPRIO_CLASS_RT``: (high) the process gets first access to the disk
+ every time. Use it with care as it can starve the entire
system. Additional priority *level* can be specified and ranges from
``0`` (highest) to ``7`` (lowest).
- * ``IOPRIO_CLASS_BE``: (best effort) the default for any process that
- hasn't set a specific I/O priority. Additional priority *level* ranges
- from ``0`` (highest) to ``7`` (lowest).
- * ``IOPRIO_CLASS_IDLE``: (lowest priority) get I/O time when no-one else
- needs the disk.
- * ``IOPRIO_CLASS_NONE``: this should be equal to ``IOPRIO_CLASS_RT``.
+ * ``IOPRIO_CLASS_BE``: (normal) the default for any process that hasn't set
+ a specific I/O priority. Additional priority *level* ranges from
+ ``0`` (highest) to ``7`` (lowest).
+ * ``IOPRIO_CLASS_IDLE``: (low) get I/O time when no-one else needs the disk.
+ No additional *value* is accepted.
+ * ``IOPRIO_CLASS_NONE``: returned when no priority was previously set.
- macOS:
+ macOS (see `getiopolicy_np`_ manual):
- * ``IOPOL_IMPORTANT``: highest priority
- * ``IOPOL_DEFAULT``: the default
+ * ``IOPOL_IMPORTANT``: highest priority.
+ * ``IOPOL_DEFAULT``: the default.
* ``IOPOL_STANDARD``, ``IOPOL_UTILITY``, ``IOPOL_THROTTLE``:
- various levels of low priority (refer to man page)
- * ``IOPOL_PASSIVE``: lowest priority
+ various levels of low priority (refer to man page).
+ * ``IOPOL_PASSIVE``: lowest priority.
Windows:
- * ``REALTIME_PRIORITY_CLASS``: highest priority
- * ``HIGH_PRIORITY_CLASS``: high priority
- * ``ABOVE_NORMAL_PRIORITY_CLASS``: something between high and normal
- * ``NORMAL_PRIORITY_CLASS``: the default
- * ``BELOW_NORMAL_PRIORITY_CLASS``: something between normal and low
- * ``IDLE_PRIORITY_CLASS``: lowest possible priority
+ * ``IOPRIO_HIGH``: highest priority.
+ * ``IOPRIO_NORMAL``: default priority.
+ * ``IOPRIO_LOW``: low priority.
+ * ``IOPRIO_VERYLOW``: lowest priority.
Here's an example on how to set the highest I/O priority depending on what
platform you're on::
@@ -1279,13 +1276,16 @@ Process class
elif psutil.MACOS:
p.ionice(psutil.IOPOL_IMPORTANT)
else: # Windows
- p.ionice(psutil.REALTIME_PRIORITY_CLASS)
+ p.ionice(psutil.IOPRIO_HIGH)
p.ionice() # get
Availability: Linux, macOS, Windows Vista+
.. versionchanged:: 5.6.2 added macOS support
+ .. versionchanged:: 5.6.2 Windows accepts mew ``IOPRIO_`` constants
+ including new IOPRIO_HIGH.
+
.. method:: rlimit(resource, limits=None)
Get or set process resource limits (see `man prlimit`_). *resource* is one
@@ -2187,10 +2187,18 @@ Constants
Availability: Linux
- .. versionchanged::
- 3.0.0 on Python >= 3.4 these constants are
- `enums <https://docs.python.org/3/library/enum.html#module-enum>`__
- instead of a plain integer.
+.. data:: IOPRIO_VERYLOW
+.. data:: IOPRIO_LOW
+.. data:: IOPRIO_NORMAL
+.. data:: IOPRIO_HIGH
+
+ A set of integers representing the I/O priority of a process on Linux.
+ They can be used in conjunction with :meth:`psutil.Process.ionice()` to get
+ or set process I/O priority.
+
+ Availability: Windows
+
+ .. versionadded:: 5.6.2
.. _const-iopol:
.. data:: IOPOL_DEFAULT
@@ -2207,6 +2215,8 @@ Constants
Availability: macOS
+ .. versionadded:: 5.6.2
+
.. _const-rlimit:
.. data:: RLIM_INFINITY
.. data:: RLIMIT_AS
@@ -2905,6 +2915,7 @@ Timeline
.. _`netstat.py`: https://github.com/giampaolo/psutil/blob/master/scripts/netstat.py.
.. _`nettop.py`: https://github.com/giampaolo/psutil/blob/master/scripts/nettop.py
.. _`open`: https://docs.python.org/3/library/functions.html#open
+.. _`getiopolicy_np`: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/getiopolicy_np.3.html
.. _`os.cpu_count`: https://docs.python.org/3/library/os.html#os.cpu_count
.. _`os.getloadavg`: https://docs.python.org/3/library/os.html#os.getloadavg
.. _`os.getpid`: https://docs.python.org/3/library/os.html#os.getpid
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 18fbe71b..5bf1305a 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -146,6 +146,10 @@ elif WINDOWS:
from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
from ._pswindows import CONN_DELETE_TCB # NOQA
+ from ._pswindows import IOPRIO_VERYLOW # NOQA
+ from ._pswindows import IOPRIO_LOW # NOQA
+ from ._pswindows import IOPRIO_NORMAL # NOQA
+ from ._pswindows import IOPRIO_HIGH # NOQA
elif MACOS:
from . import _psosx as _psplatform
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index ecba4139..6c58cf2c 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -41,7 +41,6 @@ from ._common import supports_ipv6
from ._common import usage_percent
from ._compat import b
from ._compat import basestring
-from ._compat import long
from ._compat import PY3
if sys.version_info >= (3, 4):
@@ -1987,35 +1986,12 @@ class Process(object):
@wrap_exceptions
def ionice_set(self, ioclass, value):
- if value is not None:
- if not PY3 and not isinstance(value, (int, long)):
- msg = "value argument is not an integer (gor %r)" % value
- raise TypeError(msg)
- if not 0 <= value <= 7:
- raise ValueError(
- "value argument range expected is between 0 and 7")
-
- if ioclass in (IOPRIO_CLASS_NONE, None):
- if value:
- msg = "can't specify value with IOPRIO_CLASS_NONE " \
- "(got %r)" % value
- raise ValueError(msg)
- ioclass = IOPRIO_CLASS_NONE
+ if value is None:
value = 0
- elif ioclass == IOPRIO_CLASS_IDLE:
- if value:
- msg = "can't specify value with IOPRIO_CLASS_IDLE " \
- "(got %r)" % value
- raise ValueError(msg)
- value = 0
- elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE):
- if value is None:
- # TODO: add comment explaining why this is 4 (?)
- value = 4
- else:
- # otherwise we would get OSError(EVINAL)
- raise ValueError("invalid ioclass argument %r" % ioclass)
-
+ if value and ioclass == IOPRIO_CLASS_IDLE:
+ raise ValueError("IOPRIO_CLASS_IDLE accepts no value")
+ if value < 0 or value > 7:
+ raise ValueError("value not in 0-7 range")
return cext.proc_ioprio_set(self.pid, ioclass, value)
if HAS_PRLIMIT:
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index 4b6ab399..c6e37bc2 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -62,7 +62,7 @@ PyErr_SetFromOSErrnoWithSyscall(const char *syscall) {
char fullmsg[1024];
#ifdef _WIN32
- sprintf(fullmsg, "originated from %s", syscall);
+ sprintf(fullmsg, "(originated from %s)", syscall);
PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg);
#else
PyObject *exc;
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 4dfae2d5..a1a68857 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -3,7 +3,15 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
- * Windows platform-specific module methods for _psutil_windows
+ * Windows platform-specific module methods for _psutil_windows.
+ *
+ * List of undocumented Windows NT APIs which are used in here and in
+ * other modules:
+ * - NtQuerySystemInformation
+ * - NtQueryInformationProcess
+ * - NtQueryObject
+ * - NtSuspendProcess
+ * - NtResumeProcess
*/
// Fixes clash between winsock2.h and windows.h
@@ -797,8 +805,8 @@ psutil_GetProcWsetInformation(
}
else {
PyErr_Clear();
- psutil_debug("NtQueryVirtualMemory failed with %i", status);
- PyErr_SetString(PyExc_RuntimeError, "NtQueryVirtualMemory failed");
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryVirtualMemory(MemoryWorkingSetInformation)");
}
HeapFree(GetProcessHeap(), 0, buffer);
return 1;
@@ -946,8 +954,11 @@ psutil_per_cpu_times(PyObject *self, PyObject *args) {
sppi,
ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
- if (status != 0) {
- PyErr_SetFromWindowsErr(0);
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status,
+ "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"
+ );
goto error;
}
@@ -1025,7 +1036,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
static PyObject *
psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) {
long pid;
- int ret;
+ NTSTATUS status;
HANDLE hProcess;
PyObject* suspend;
@@ -1037,15 +1048,15 @@ psutil_proc_suspend_or_resume(PyObject *self, PyObject *args) {
return NULL;
if (PyObject_IsTrue(suspend))
- ret = psutil_NtSuspendProcess(hProcess);
+ status = psutil_NtSuspendProcess(hProcess);
else
- ret = psutil_NtResumeProcess(hProcess);
+ status = psutil_NtResumeProcess(hProcess);
- if (ret != 0) {
- PyErr_SetFromWindowsErr(0);
+ if (! NT_SUCCESS(status)) {
CloseHandle(hProcess);
- return NULL;
+ return psutil_SetFromNTStatusErr(status, "NtSuspend|ResumeProcess");
}
+
CloseHandle(hProcess);
Py_RETURN_NONE;
}
@@ -1339,6 +1350,7 @@ error:
// https://msdn.microsoft.com/library/aa365928.aspx
+// TODO properly handle return code
static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call,
ULONG address_family,
PVOID * data, DWORD * size)
@@ -1373,6 +1385,7 @@ static DWORD __GetExtendedTcpTable(_GetExtendedTcpTable call,
// https://msdn.microsoft.com/library/aa365930.aspx
+// TODO properly check return value
static DWORD __GetExtendedUdpTable(_GetExtendedUdpTable call,
ULONG address_family,
PVOID * data, DWORD * size)
@@ -1859,20 +1872,26 @@ psutil_proc_io_priority_get(PyObject *self, PyObject *args) {
long pid;
HANDLE hProcess;
DWORD IoPriority;
+ NTSTATUS status;
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
+
hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
if (hProcess == NULL)
return NULL;
- psutil_NtQueryInformationProcess(
+
+ status = psutil_NtQueryInformationProcess(
hProcess,
ProcessIoPriority,
&IoPriority,
sizeof(DWORD),
NULL
);
+
CloseHandle(hProcess);
+ if (! NT_SUCCESS(status))
+ return psutil_SetFromNTStatusErr(status, "NtQueryInformationProcess");
return Py_BuildValue("i", IoPriority);
}
@@ -1885,15 +1904,17 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
long pid;
DWORD prio;
HANDLE hProcess;
+ NTSTATUS status;
DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION;
if (! PyArg_ParseTuple(args, "li", &pid, &prio))
return NULL;
+
hProcess = psutil_handle_from_pid(pid, access);
if (hProcess == NULL)
return NULL;
- psutil_NtSetInformationProcess(
+ status = psutil_NtSetInformationProcess(
hProcess,
ProcessIoPriority,
(PVOID)&prio,
@@ -1901,6 +1922,8 @@ psutil_proc_io_priority_set(PyObject *self, PyObject *args) {
);
CloseHandle(hProcess);
+ if (! NT_SUCCESS(status))
+ return psutil_SetFromNTStatusErr(status, "NtSetInformationProcess");
Py_RETURN_NONE;
}
#endif
@@ -3217,9 +3240,9 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
spi,
ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION),
NULL);
- if (status != 0) {
- PyErr_SetFromOSErrnoWithSyscall(
- "NtQuerySystemInformation(SYSTEM_PERFORMANCE_INFORMATION)");
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemPerformanceInformation)");
goto error;
}
@@ -3236,9 +3259,9 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
InterruptInformation,
ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION),
NULL);
- if (status != 0) {
- PyErr_SetFromOSErrnoWithSyscall(
- "NtQuerySystemInformation(SYSTEM_INTERRUPT_INFORMATION)");
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemInterruptInformation)");
goto error;
}
for (i = 0; i < ncpus; i++) {
@@ -3258,9 +3281,10 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
sppi,
ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
NULL);
- if (status != 0) {
- PyErr_SetFromOSErrnoWithSyscall(
- "NtQuerySystemInformation(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)");
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status,
+ "NtQuerySystemInformation(SystemProcessorPerformanceInformation)");
goto error;
}
@@ -3661,7 +3685,8 @@ void init_psutil_windows(void)
module, "ERROR_INVALID_NAME", ERROR_INVALID_NAME);
PyModule_AddIntConstant(
module, "ERROR_SERVICE_DOES_NOT_EXIST", ERROR_SERVICE_DOES_NOT_EXIST);
-
+ PyModule_AddIntConstant(
+ module, "ERROR_PRIVILEGE_NOT_HELD", ERROR_PRIVILEGE_NOT_HELD);
PyModule_AddIntConstant(
module, "WINVER", PSUTIL_WINVER);
PyModule_AddIntConstant(
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 6687770c..929e27d7 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -63,11 +63,14 @@ else:
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
__extra__all__ = [
"win_service_iter", "win_service_get",
+ # Process priority
"ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
- "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
- "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
- "CONN_DELETE_TCB",
- "AF_LINK",
+ "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS",
+ "REALTIME_PRIORITY_CLASS",
+ # IO priority
+ "IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH",
+ # others
+ "CONN_DELETE_TCB", "AF_LINK",
]
@@ -76,10 +79,6 @@ __extra__all__ = [
# =====================================================================
CONN_DELETE_TCB = "DELETE_TCB"
-ACCESS_DENIED_ERRSET = frozenset([errno.EPERM, errno.EACCES,
- cext.ERROR_ACCESS_DENIED])
-NO_SUCH_SERVICE_ERRSET = frozenset([cext.ERROR_INVALID_NAME,
- cext.ERROR_SERVICE_DOES_NOT_EXIST])
HAS_PROC_IO_PRIORITY = hasattr(cext, "proc_io_priority_get")
@@ -116,6 +115,19 @@ if enum is not None:
globals().update(Priority.__members__)
+if enum is None:
+ IOPRIO_VERYLOW = 0
+ IOPRIO_LOW = 1
+ IOPRIO_NORMAL = 2
+ IOPRIO_HIGH = 3
+else:
+ class IOPriority(enum.IntEnum):
+ IOPRIO_VERYLOW = 0
+ IOPRIO_LOW = 1
+ IOPRIO_NORMAL = 2
+ IOPRIO_HIGH = 3
+ globals().update(IOPriority.__members__)
+
pinfo_map = dict(
num_handles=0,
ctx_switches=1,
@@ -533,14 +545,14 @@ class WindowsService(object):
"""
try:
yield
- except WindowsError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
+ except OSError as err:
+ if is_permission_err(err):
raise AccessDenied(
pid=None, name=self._name,
msg="service %r is not querable (not enough privileges)" %
self._name)
- elif err.errno in NO_SUCH_SERVICE_ERRSET or \
- err.winerror in NO_SUCH_SERVICE_ERRSET:
+ elif err.winerror in (cext.ERROR_INVALID_NAME,
+ cext.ERROR_SERVICE_DOES_NOT_EXIST):
raise NoSuchProcess(
pid=None, name=self._name,
msg="service %r does not exist)" % self._name)
@@ -657,20 +669,35 @@ pid_exists = cext.pid_exists
ppid_map = cext.ppid_map # used internally by Process.children()
+def is_permission_err(exc):
+ """Return True if this is a permission error."""
+ assert isinstance(exc, OSError), exc
+ # On Python 2 OSError doesn't always have 'winerror'. Sometimes
+ # it does, in which case the original exception was WindowsError
+ # (which is a subclass of OSError).
+ return exc.errno in (errno.EPERM, errno.EACCES) or \
+ getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED,
+ cext.ERROR_PRIVILEGE_NOT_HELD)
+
+
+def convert_oserror(exc, pid=None, name=None):
+ """Convert OSError into NoSuchProcess or AccessDenied."""
+ assert isinstance(exc, OSError), exc
+ if is_permission_err(exc):
+ return AccessDenied(pid=pid, name=name)
+ if exc.errno == errno.ESRCH:
+ return NoSuchProcess(pid=pid, name=name)
+ raise exc
+
+
def wrap_exceptions(fun):
- """Decorator which translates bare OSError and WindowsError
- exceptions into NoSuchProcess and AccessDenied.
- """
+ """Decorator which converts OSError into NoSuchProcess or AccessDenied."""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
- raise AccessDenied(self.pid, self._name)
- if err.errno == errno.ESRCH:
- raise NoSuchProcess(self.pid, self._name)
- raise
+ raise convert_oserror(err, pid=self.pid, name=self._name)
return wrapper
@@ -744,7 +771,7 @@ class Process(object):
try:
ret = cext.proc_cmdline(self.pid, use_peb=True)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
+ if is_permission_err(err):
ret = cext.proc_cmdline(self.pid, use_peb=False)
else:
raise
@@ -772,7 +799,7 @@ class Process(object):
try:
return cext.proc_memory_info(self.pid)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
+ if is_permission_err(err):
# TODO: the C ext can probably be refactored in order
# to get this from cext.proc_info()
info = self.oneshot_info()
@@ -813,11 +840,7 @@ class Process(object):
except OSError as err:
# XXX - can't use wrap_exceptions decorator as we're
# returning a generator; probably needs refactoring.
- if err.errno in ACCESS_DENIED_ERRSET:
- raise AccessDenied(self.pid, self._name)
- if err.errno == errno.ESRCH:
- raise NoSuchProcess(self.pid, self._name)
- raise
+ raise convert_oserror(err, self.pid, self._name)
else:
for addr, perm, path, rss in raw:
path = convert_dos_path(path)
@@ -893,7 +916,7 @@ class Process(object):
try:
return cext.proc_create_time(self.pid)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
+ if is_permission_err(err):
return self.oneshot_info()[pinfo_map['create_time']]
raise
@@ -915,12 +938,11 @@ class Process(object):
try:
user, system = cext.proc_cpu_times(self.pid)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
- info = self.oneshot_info()
- user = info[pinfo_map['user_time']]
- system = info[pinfo_map['kernel_time']]
- else:
+ if not is_permission_err(err):
raise
+ info = self.oneshot_info()
+ user = info[pinfo_map['user_time']]
+ system = info[pinfo_map['kernel_time']]
# Children user/system times are not retrievable (set to 0).
return _common.pcputimes(user, system, 0.0, 0.0)
@@ -979,35 +1001,36 @@ class Process(object):
if HAS_PROC_IO_PRIORITY:
@wrap_exceptions
def ionice_get(self):
- return cext.proc_io_priority_get(self.pid)
+ ret = cext.proc_io_priority_get(self.pid)
+ if enum is not None:
+ ret = IOPriority(ret)
+ return ret
@wrap_exceptions
- def ionice_set(self, value, _):
- if _:
- raise TypeError("set_proc_ionice() on Windows takes only "
- "1 argument (2 given)")
- if value not in (2, 1, 0):
- raise ValueError("value must be 2 (normal), 1 (low) or 0 "
- "(very low); got %r" % value)
- return cext.proc_io_priority_set(self.pid, value)
+ def ionice_set(self, ioclass, value):
+ if value:
+ raise TypeError("value argument not accepted on Windows")
+ if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL,
+ IOPRIO_HIGH):
+ raise ValueError("%s is not a valid priority" % ioclass)
+ cext.proc_io_priority_set(self.pid, ioclass)
@wrap_exceptions
def io_counters(self):
try:
ret = cext.proc_io_counters(self.pid)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
- info = self.oneshot_info()
- ret = (
- info[pinfo_map['io_rcount']],
- info[pinfo_map['io_wcount']],
- info[pinfo_map['io_rbytes']],
- info[pinfo_map['io_wbytes']],
- info[pinfo_map['io_count_others']],
- info[pinfo_map['io_bytes_others']],
- )
- else:
+ if not is_permission_err(err):
raise
+ info = self.oneshot_info()
+ ret = (
+ info[pinfo_map['io_rcount']],
+ info[pinfo_map['io_wcount']],
+ info[pinfo_map['io_rbytes']],
+ info[pinfo_map['io_wbytes']],
+ info[pinfo_map['io_count_others']],
+ info[pinfo_map['io_bytes_others']],
+ )
return pio(*ret)
@wrap_exceptions
@@ -1055,7 +1078,7 @@ class Process(object):
try:
return cext.proc_num_handles(self.pid)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
+ if is_permission_err(err):
return self.oneshot_info()[pinfo_map['num_handles']]
raise
diff --git a/psutil/arch/windows/global.c b/psutil/arch/windows/global.c
index 9ef92092..4d8526e3 100644
--- a/psutil/arch/windows/global.c
+++ b/psutil/arch/windows/global.c
@@ -18,6 +18,14 @@
int PSUTIL_WINVER;
SYSTEM_INFO PSUTIL_SYSTEM_INFO;
+#define NT_FACILITY_MASK 0xfff
+#define NT_FACILITY_SHIFT 16
+#define NT_FACILITY(Status) \
+ ((((ULONG)(Status)) >> NT_FACILITY_SHIFT) & NT_FACILITY_MASK)
+#define NT_NTWIN32(status) (NT_FACILITY(Status) == FACILITY_WIN32)
+#define WIN32_FROM_NTSTATUS(Status) (((ULONG)(Status)) & 0xffff)
+
+
// A wrapper around GetModuleHandle and GetProcAddress.
PVOID
psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) {
@@ -60,6 +68,26 @@ psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) {
}
+/*
+ * Convert a NTSTATUS value to a Win32 error code and set the proper
+ * Python exception.
+ */
+PVOID
+psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) {
+ ULONG err;
+ char fullmsg[1024];
+
+ if (NT_NTWIN32(Status))
+ err = WIN32_FROM_NTSTATUS(Status);
+ else
+ err = psutil_RtlNtStatusToDosErrorNoTeb(Status);
+ // if (GetLastError() != 0)
+ // err = GetLastError();
+ sprintf(fullmsg, "(originated from %s)", syscall);
+ return PyErr_SetFromWindowsErrWithFilename(err, fullmsg);
+}
+
+
static int
psutil_loadlibs() {
/*
@@ -127,6 +155,11 @@ psutil_loadlibs() {
if (! psutil_NtQueryVirtualMemory)
return 1;
+ psutil_RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib(
+ "ntdll", "RtlNtStatusToDosErrorNoTeb");
+ if (! psutil_RtlNtStatusToDosErrorNoTeb)
+ return 1;
+
/*
* Optional.
*/
diff --git a/psutil/arch/windows/global.h b/psutil/arch/windows/global.h
index fb24bac9..10ae6405 100644
--- a/psutil/arch/windows/global.h
+++ b/psutil/arch/windows/global.h
@@ -23,6 +23,7 @@ extern SYSTEM_INFO PSUTIL_SYSTEM_INFO;
int psutil_load_globals();
PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname);
PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname);
+PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall);
_NtQuerySystemInformation \
psutil_NtQuerySystemInformation;
@@ -71,3 +72,6 @@ _NtResumeProcess \
_NtQueryVirtualMemory \
psutil_NtQueryVirtualMemory;
+
+_RtlNtStatusToDosErrorNoTeb \
+ psutil_RtlNtStatusToDosErrorNoTeb;
diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h
index 178f9866..b6f23d99 100644
--- a/psutil/arch/windows/ntextapi.h
+++ b/psutil/arch/windows/ntextapi.h
@@ -500,4 +500,8 @@ typedef NTSTATUS (NTAPI *_NtQueryVirtualMemory) (
PSIZE_T ReturnLength
);
+typedef ULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) (
+ NTSTATUS status
+);
+
#endif // __NTEXTAPI_H__
diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c
index 8b899972..5966669e 100644
--- a/psutil/arch/windows/process_handles.c
+++ b/psutil/arch/windows/process_handles.c
@@ -52,6 +52,7 @@ psutil_wait_thread(LPVOID lpvParam) {
while (TRUE) {
WaitForSingleObject(g_hEvtStart, INFINITE);
+ // TODO: return code not checked
g_status = psutil_NtQueryObject(
g_hFile,
ObjectNameInformation,
@@ -159,8 +160,9 @@ psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) {
&dwRet)) == STATUS_INFO_LENGTH_MISMATCH);
// NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH
- if (!NT_SUCCESS(status)) {
- PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status));
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemExtendedHandleInformation)");
error = TRUE;
goto cleanup;
}
@@ -355,8 +357,9 @@ psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) {
&dwRet)) == STATUS_INFO_LENGTH_MISMATCH);
// NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH
- if (!NT_SUCCESS(status)) {
- PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status));
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemExtendedHandleInformation)");
error = TRUE;
goto cleanup;
}
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index 946a01cb..3b3c677e 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -483,6 +483,7 @@ psutil_get_process_data(long pid,
BOOL theyAreWow64;
#endif
DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+ NTSTATUS status;
hProcess = psutil_handle_from_pid(pid, access);
if (hProcess == NULL)
@@ -491,15 +492,16 @@ psutil_get_process_data(long pid,
#ifdef _WIN64
/* 64 bit case. Check if the target is a 32 bit process running in WoW64
* mode. */
- if (! NT_SUCCESS(psutil_NtQueryInformationProcess(
- hProcess,
- ProcessWow64Information,
- &ppeb32,
- sizeof(LPVOID),
- NULL)))
- {
- PyErr_SetFromOSErrnoWithSyscall(
- "NtQueryInformationProcess(ProcessWow64Information)");
+ status = psutil_NtQueryInformationProcess(
+ hProcess,
+ ProcessWow64Information,
+ &ppeb32,
+ sizeof(LPVOID),
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessWow64Information)");
goto error;
}
@@ -633,18 +635,20 @@ psutil_get_process_data(long pid,
PEB_ peb;
RTL_USER_PROCESS_PARAMETERS_ procParameters;
- if (! NT_SUCCESS(psutil_NtQueryInformationProcess(
- hProcess,
- ProcessBasicInformation,
- &pbi,
- sizeof(pbi),
- NULL)))
- {
- PyErr_SetFromOSErrnoWithSyscall(
- "NtQueryInformationProcess(ProcessBasicInformation)");
+ status = psutil_NtQueryInformationProcess(
+ hProcess,
+ ProcessBasicInformation,
+ &pbi,
+ sizeof(pbi),
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessBasicInformation)");
goto error;
}
+
// read peb
if (!ReadProcessMemory(hProcess,
pbi.PebBaseAddress,
@@ -767,10 +771,12 @@ psutil_cmdline_query_proc(long pid, WCHAR **pdata, SIZE_T *psize) {
NULL,
0,
&bufLen);
+
if (status != STATUS_BUFFER_OVERFLOW && \
status != STATUS_BUFFER_TOO_SMALL && \
status != STATUS_INFO_LENGTH_MISMATCH) {
- PyErr_SetFromOSErrnoWithSyscall("NtQueryInformationProcess(0)");
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessBasicInformation)");
goto error;
}
@@ -789,8 +795,9 @@ psutil_cmdline_query_proc(long pid, WCHAR **pdata, SIZE_T *psize) {
bufLen,
&bufLen
);
- if (! NT_SUCCESS(status)) {
- PyErr_SetFromOSErrnoWithSyscall("NtQueryInformationProcess(withlen)");
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessCommandLineInformation)");
goto error;
}
@@ -971,9 +978,9 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
}
}
- if (status != 0) {
- PyErr_Format(
- PyExc_RuntimeError, "NtQuerySystemInformation() syscall failed");
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemProcessInformation)");
goto error;
}
diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py
index 9e19d198..1a28aa43 100755
--- a/psutil/tests/runner.py
+++ b/psutil/tests/runner.py
@@ -24,11 +24,13 @@ except ImportError:
import psutil
from psutil._common import memoize
+from psutil.tests import safe_rmpath
from psutil.tests import TOX
HERE = os.path.abspath(os.path.dirname(__file__))
VERBOSITY = 1 if TOX else 2
+FAILED_TESTS_FNAME = '.failed-tests.txt'
if os.name == 'posix':
GREEN = 1
RED = 2
@@ -157,15 +159,38 @@ def get_suite(name=None):
return suite
-def run(name=None):
+def get_suite_from_failed():
+ # ...from previously failed test run
+ suite = unittest.TestSuite()
+ if not os.path.isfile(FAILED_TESTS_FNAME):
+ return suite
+ with open(FAILED_TESTS_FNAME, 'rt') as f:
+ names = f.read().split()
+ for n in names:
+ suite.addTest(unittest.defaultTestLoader.loadTestsFromName(n))
+ return suite
+
+
+def save_failed_tests(result):
+ if result.wasSuccessful():
+ return safe_rmpath(FAILED_TESTS_FNAME)
+ with open(FAILED_TESTS_FNAME, 'wt') as f:
+ for t in result.errors + result.failures:
+ tname = str(t[0])
+ f.write(tname + '\n')
+
+
+def run(name=None, last_failed=False):
setup_tests()
runner = ColouredRunner(verbosity=VERBOSITY)
+ suite = get_suite_from_failed() if last_failed else get_suite(name)
try:
- result = runner.run(get_suite(name))
+ result = runner.run(suite)
except (KeyboardInterrupt, SystemExit) as err:
print("received %s" % err.__class__.__name__, file=sys.stderr)
runner.result.printErrors()
sys.exit(1)
else:
+ save_failed_tests(result)
success = result.wasSuccessful()
sys.exit(0 if success else 1)
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index d39213cb..d84c0ba3 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -625,8 +625,11 @@ class TestFetchAllProcesses(unittest.TestCase):
# commented as on Linux we might get
# '/foo/bar (deleted)'
# assert os.path.exists(nt.path), nt.path
- elif fname in ('addr', 'perms'):
- assert value
+ elif fname == 'addr':
+ assert value, repr(value)
+ elif fname == 'perms':
+ if not WINDOWS:
+ assert value, repr(value)
else:
self.assertIsInstance(value, (int, long))
self.assertGreaterEqual(value, 0)
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index f38803a0..873a6975 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -43,7 +43,6 @@ from psutil.tests import create_proc_children_pair
from psutil.tests import create_zombie_proc
from psutil.tests import enum
from psutil.tests import get_test_subprocess
-from psutil.tests import get_winver
from psutil.tests import HAS_CPU_AFFINITY
from psutil.tests import HAS_ENVIRON
from psutil.tests import HAS_IONICE
@@ -67,7 +66,6 @@ from psutil.tests import ThreadTask
from psutil.tests import TRAVIS
from psutil.tests import unittest
from psutil.tests import wait_for_pid
-from psutil.tests import WIN_VISTA
# ===================================================================
@@ -351,71 +349,38 @@ class TestProcess(unittest.TestCase):
self.assertGreaterEqual(io2[i], 0)
@unittest.skipIf(not HAS_IONICE, "not supported")
- @unittest.skipIf(WINDOWS and get_winver() < WIN_VISTA, 'not supported')
- def test_ionice(self):
+ @unittest.skipIf(not LINUX, "Linux only")
+ def test_ionice_linux(self):
p = psutil.Process()
- if LINUX:
- from psutil import (IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT,
- IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE)
- self.assertEqual(IOPRIO_CLASS_NONE, 0)
- self.assertEqual(IOPRIO_CLASS_RT, 1)
- self.assertEqual(IOPRIO_CLASS_BE, 2)
- self.assertEqual(IOPRIO_CLASS_IDLE, 3)
- try:
- p.ionice(2)
- ioclass, value = p.ionice()
- if enum is not None:
- self.assertIsInstance(ioclass, enum.IntEnum)
- self.assertEqual(ioclass, 2)
- self.assertEqual(value, 4)
- #
- p.ionice(3)
- ioclass, value = p.ionice()
- self.assertEqual(ioclass, 3)
- self.assertEqual(value, 0)
- #
- p.ionice(2, 0)
- ioclass, value = p.ionice()
- self.assertEqual(ioclass, 2)
- self.assertEqual(value, 0)
- p.ionice(2, 7)
- ioclass, value = p.ionice()
- self.assertEqual(ioclass, 2)
- self.assertEqual(value, 7)
- finally:
- p.ionice(IOPRIO_CLASS_NONE)
- if MACOS:
- names = ("IOPOL_IMPORTANT", "IOPOL_STANDARD", "IOPOL_UTILITY",
- "IOPOL_THROTTLE", "IOPOL_PASSIVE")
- original = p.ionice()
- try:
- for name in names:
- if name == "IOPOL_IMPORTANT":
- # XXX has no effect (?!?)
- continue
- value = getattr(psutil, name)
- p.ionice(value)
- self.assertEqual(p.ionice(), value)
- finally:
- p.ionice(original)
- else:
- original = p.ionice()
- self.assertIsInstance(original, int)
- try:
- value = 0 # very low
- if original == value:
- value = 1 # low
- p.ionice(value)
- self.assertEqual(p.ionice(), value)
- finally:
- p.ionice(original)
-
- @unittest.skipIf(not HAS_IONICE, "not supported")
- @unittest.skipIf(WINDOWS and get_winver() < WIN_VISTA, 'not supported')
- def test_ionice_errs(self):
- sproc = get_test_subprocess()
- p = psutil.Process(sproc.pid)
- if LINUX:
+ self.assertEqual(p.ionice()[0], psutil.IOPRIO_CLASS_NONE)
+ self.assertEqual(psutil.IOPRIO_CLASS_NONE, 0)
+ self.assertEqual(psutil.IOPRIO_CLASS_RT, 1) # high
+ self.assertEqual(psutil.IOPRIO_CLASS_BE, 2) # normal
+ self.assertEqual(psutil.IOPRIO_CLASS_IDLE, 3) # low
+ try:
+ # low
+ p.ionice(psutil.IOPRIO_CLASS_IDLE)
+ self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_IDLE, 0))
+ with self.assertRaises(ValueError): # accepts no value
+ p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7)
+ # normal
+ p.ionice(psutil.IOPRIO_CLASS_BE)
+ self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 0))
+ p.ionice(psutil.IOPRIO_CLASS_BE, value=7)
+ self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 7))
+ with self.assertRaises(ValueError):
+ p.ionice(psutil.IOPRIO_CLASS_BE, value=8)
+ # high
+ if os.getuid() == 0: # root
+ p.ionice(psutil.IOPRIO_CLASS_RT)
+ self.assertEqual(tuple(p.ionice()),
+ (psutil.IOPRIO_CLASS_RT, 0))
+ p.ionice(psutil.IOPRIO_CLASS_RT, value=7)
+ self.assertEqual(tuple(p.ionice()),
+ (psutil.IOPRIO_CLASS_RT, 7))
+ with self.assertRaises(ValueError):
+ p.ionice(psutil.IOPRIO_CLASS_IDLE, value=8)
+ # errs
self.assertRaises(ValueError, p.ionice, 2, 10)
self.assertRaises(ValueError, p.ionice, 2, -1)
self.assertRaises(ValueError, p.ionice, 4)
@@ -429,11 +394,55 @@ class TestProcess(unittest.TestCase):
self.assertRaisesRegex(
ValueError, "'ioclass' argument must be specified",
p.ionice, value=1)
- else:
- if not MACOS:
- # ionice() can only be set for the current process
- self.assertRaises(TypeError, p.ionice, 2, 1)
- self.assertRaises(ValueError, p.ionice, 3)
+ finally:
+ p.ionice(psutil.IOPRIO_CLASS_BE)
+
+ @unittest.skipIf(not HAS_IONICE, "not supported")
+ @unittest.skipIf(not WINDOWS, 'Windows only')
+ def test_ionice_win(self):
+ p = psutil.Process()
+ self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL)
+ try:
+ # base
+ p.ionice(psutil.IOPRIO_VERYLOW)
+ self.assertEqual(p.ionice(), psutil.IOPRIO_VERYLOW)
+ p.ionice(psutil.IOPRIO_LOW)
+ self.assertEqual(p.ionice(), psutil.IOPRIO_LOW)
+ try:
+ p.ionice(psutil.IOPRIO_HIGH)
+ except psutil.AccessDenied:
+ pass
+ else:
+ self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH)
+ # errs
+ self.assertRaisesRegex(
+ TypeError, "value argument not accepted on Windows",
+ p.ionice, psutil.IOPRIO_NORMAL, value=1)
+ self.assertRaisesRegex(
+ ValueError, "is not a valid priority",
+ p.ionice, psutil.IOPRIO_HIGH + 1)
+ finally:
+ p.ionice(psutil.IOPRIO_NORMAL)
+ self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL)
+
+ @unittest.skipIf(not HAS_IONICE, "not supported")
+ @unittest.skipIf(not MACOS, 'macOS only')
+ def test_ionice_macos(self):
+ names = ("IOPOL_IMPORTANT", "IOPOL_STANDARD", "IOPOL_UTILITY",
+ "IOPOL_THROTTLE", "IOPOL_PASSIVE")
+ p = psutil.Process()
+ original = p.ionice()
+ try:
+ for n in names:
+ with self.subTest(name=n):
+ if n == "IOPOL_IMPORTANT":
+ # XXX has no effect (?!?)
+ continue
+ value = getattr(psutil, n)
+ p.ionice(value)
+ self.assertEqual(p.ionice(), value)
+ finally:
+ p.ionice(original)
@unittest.skipIf(not HAS_RLIMIT, "not supported")
def test_rlimit_get(self):
diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py
index a3a6b61d..70c99b4b 100755
--- a/psutil/tests/test_windows.py
+++ b/psutil/tests/test_windows.py
@@ -664,17 +664,15 @@ class TestDualProcessImplementation(unittest.TestCase):
assert fun.called
def test_cmdline(self):
- from psutil._pswindows import ACCESS_DENIED_ERRSET
+ from psutil._pswindows import convert_oserror
for pid in psutil.pids():
try:
a = cext.proc_cmdline(pid, use_peb=True)
b = cext.proc_cmdline(pid, use_peb=False)
except OSError as err:
- if err.errno in ACCESS_DENIED_ERRSET:
- pass
- elif err.errno == errno.ESRCH:
- pass # NSP
- else:
+ err = convert_oserror(err)
+ if not isinstance(err, (psutil.AccessDenied,
+ psutil.NoSuchProcess)):
raise
else:
self.assertEqual(a, b)
@@ -837,7 +835,8 @@ class TestServices(unittest.TestCase):
# test NoSuchProcess
service = psutil.win_service_get(name)
exc = WindowsError(
- psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST, "")
+ 0, "", 0,
+ psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST)
with mock.patch("psutil._psplatform.cext.winservice_query_status",
side_effect=exc):
self.assertRaises(psutil.NoSuchProcess, service.status)
@@ -847,7 +846,8 @@ class TestServices(unittest.TestCase):
# test AccessDenied
exc = WindowsError(
- psutil._psplatform.cext.ERROR_ACCESS_DENIED, "")
+ 0, "", 0,
+ psutil._psplatform.cext.ERROR_ACCESS_DENIED)
with mock.patch("psutil._psplatform.cext.winservice_query_status",
side_effect=exc):
self.assertRaises(psutil.AccessDenied, service.status)
diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py
index cbdeebdc..75b4c348 100755
--- a/scripts/internal/winmake.py
+++ b/scripts/internal/winmake.py
@@ -318,6 +318,7 @@ def clean():
"*.~",
"*__pycache__",
".coverage",
+ ".failed-tests.txt",
".tox",
)
safe_rmtree("build")
@@ -440,6 +441,15 @@ def test_by_name():
@cmd
+def test_failed():
+ """Re-run tests which failed on last run."""
+ install()
+ test_setup()
+ sh('%s -c "import psutil.tests.runner as r; r.run(last_failed=True)"' % (
+ PYTHON))
+
+
+@cmd
def test_script():
"""Quick way to test a script"""
try: