Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Win] Process IO priority constants + high priority #1479

Merged
merged 10 commits into from
Apr 4, 2019
3 changes: 3 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
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**
Expand Down
26 changes: 19 additions & 7 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1245,12 +1245,15 @@ Process class
pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
>>>

On Windows only *ioclass* is used and it can be set to ``2`` (normal),
``1`` (low) or ``0`` (very low). Also it returns an integer instead of a
named tuple.
On Windows only *ioclass* is used and it can be set to ``3`` (high),
``2`` (normal), ``1`` (low) or ``0`` (very low).
Also it returns an integer instead of a named tuple.

Availability: Linux and Windows > Vista

.. versionchanged::
Windows accepts ``3`` (high) value.

.. versionchanged::
3.0.0 on Python >= 3.4 the returned ``ioclass`` constant is an
`enum <https://docs.python.org/3/library/enum.html#module-enum>`__
Expand Down Expand Up @@ -2157,10 +2160,19 @@ 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.
.. _const-ioprio:
.. 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-rlimit:
.. data:: RLIM_INFINITY
Expand Down
4 changes: 4 additions & 0 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@
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
Expand Down
3 changes: 2 additions & 1 deletion psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -3685,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(
Expand Down
50 changes: 36 additions & 14 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@
# 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",
]


Expand Down Expand Up @@ -112,6 +115,19 @@ class Priority(enum.IntEnum):

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,
Expand Down Expand Up @@ -656,8 +672,12 @@ def as_dict(self):
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 \
exc.winerror == cext.ERROR_ACCESS_DENIED
getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED,
cext.ERROR_PRIVILEGE_NOT_HELD)


def convert_oserror(exc, pid=None, name=None):
Expand Down Expand Up @@ -981,17 +1001,19 @@ def nice_set(self, value):
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):
Expand Down
57 changes: 31 additions & 26 deletions psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,29 +380,7 @@ def test_ionice_linux(self):
(psutil.IOPRIO_CLASS_RT, 7))
with self.assertRaises(ValueError):
p.ionice(psutil.IOPRIO_CLASS_IDLE, value=8)
finally:
p.ionice(psutil.IOPRIO_CLASS_BE)

@unittest.skipIf(not HAS_IONICE, "not supported")
@unittest.skipIf(not WINDOWS, 'not supported on this win version')
def test_ionice_win(self):
p = psutil.Process()
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")
def test_ionice_errs(self):
sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
if LINUX:
# errs
self.assertRaises(ValueError, p.ionice, 2, 10)
self.assertRaises(ValueError, p.ionice, 2, -1)
self.assertRaises(ValueError, p.ionice, 4)
Expand All @@ -416,9 +394,36 @@ def test_ionice_errs(self):
self.assertRaisesRegex(
ValueError, "'ioclass' argument must be specified",
p.ionice, value=1)
else:
self.assertRaises(ValueError, p.ionice, 3)
self.assertRaises(TypeError, p.ionice, 2, 1)
finally:
p.ionice(psutil.IOPRIO_CLASS_BE)

@unittest.skipIf(not HAS_IONICE, "not supported")
@unittest.skipIf(not WINDOWS, 'not supported on this win version')
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_RLIMIT, "not supported")
def test_rlimit_get(self):
Expand Down