Skip to content

Commit

Permalink
[Win] Process IO priority constants + high priority (#1479 / #1476)
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Apr 4, 2019
1 parent df45572 commit e471e7c
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 48 deletions.
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

0 comments on commit e471e7c

Please sign in to comment.