Skip to content

Commit 10f4597

Browse files
committed
Refactor priority and io priority
1 parent 71164a8 commit 10f4597

File tree

1 file changed

+118
-47
lines changed

1 file changed

+118
-47
lines changed

command_runner/__init__.py

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
__author__ = "Orsiris de Jong"
2222
__copyright__ = "Copyright (C) 2015-2025 Orsiris de Jong for NetInvent"
2323
__licence__ = "BSD 3 Clause"
24-
__version__ = "1.7.2"
25-
__build__ = "2025031001"
24+
__version__ = "1.7.3-dev"
25+
__build__ = "2025031401"
2626
__compat__ = "python2.7+"
2727

2828
import io
@@ -33,38 +33,92 @@
3333
from datetime import datetime
3434
from logging import getLogger
3535
from time import sleep
36-
import threading
3736

3837

3938
# Avoid checking os type numerous times
4039
os_name = os.name
4140

41+
42+
# Don't bother with an ImportError since we need command_runner to work without dependencies
4243
try:
4344
import psutil
44-
except ImportError:
45-
# Don't bother with an error since we need command_runner to work without dependencies
46-
pass
47-
try:
45+
4846
# Also make sure we directly import priority classes so we can reuse them
4947
if os_name == "nt":
5048
from psutil import (
51-
ABOVE_NORMAL_PRIORITY_CLASS,
49+
# ABOVE_NORMAL_PRIORITY_CLASS,
5250
BELOW_NORMAL_PRIORITY_CLASS,
5351
HIGH_PRIORITY_CLASS,
5452
IDLE_PRIORITY_CLASS,
5553
NORMAL_PRIORITY_CLASS,
5654
REALTIME_PRIORITY_CLASS,
5755
)
58-
from psutil import IOPRIO_HIGH, IOPRIO_NORMAL, IOPRIO_LOW, IOPRIO_VERYLOW
56+
from psutil import (
57+
IOPRIO_HIGH,
58+
IOPRIO_NORMAL,
59+
IOPRIO_LOW,
60+
# IOPRIO_VERYLOW,
61+
)
5962
else:
6063
from psutil import (
6164
IOPRIO_CLASS_BE,
6265
IOPRIO_CLASS_IDLE,
63-
IOPRIO_CLASS_NONE,
66+
# IOPRIO_CLASS_NONE,
6467
IOPRIO_CLASS_RT,
6568
)
6669
except (ImportError, AttributeError):
67-
pass
70+
if os_name == "nt":
71+
BELOW_NORMAL_PRIORITY_CLASS = 16384
72+
HIGH_PRIORITY_CLASS = 128
73+
NORMAL_PRIORITY_CLASS = 32
74+
REALTIME_PRIORITY_CLASS = 256
75+
IDLE_PRIORITY_CLASS = 64
76+
IOPRIO_HIGH = 3
77+
IOPRIO_NORMAL = 2
78+
IOPRIO_LOW = 1
79+
else:
80+
IOPRIO_CLASS_IDLE = 3
81+
IOPRIO_CLASS_BE = 2
82+
IOPRIO_CLASS_RT = 1
83+
84+
85+
# Python 2.7 does not have priorities defined in subprocess module, but psutil has
86+
# Since Windows and Linux use different possible values, let's simplify things by
87+
# allowing 5 process priorities: verylow (idle), low, normal, high, rt
88+
# and 3 process io priorities: low, normal, high
89+
# For IO, rt == high
90+
if os_name == "nt":
91+
PRIORITIES = {
92+
"process": {
93+
"verylow": IDLE_PRIORITY_CLASS,
94+
"low": BELOW_NORMAL_PRIORITY_CLASS,
95+
"normal": NORMAL_PRIORITY_CLASS,
96+
"high": HIGH_PRIORITY_CLASS,
97+
"rt": REALTIME_PRIORITY_CLASS,
98+
},
99+
"io": {
100+
"low": IOPRIO_LOW,
101+
"normal": IOPRIO_NORMAL,
102+
"high": IOPRIO_HIGH,
103+
},
104+
}
105+
else:
106+
PRIORITIES = {
107+
"process": {
108+
"verylow": 20,
109+
"low": 15,
110+
"normal": 0,
111+
"high": -15,
112+
"rt": -20,
113+
},
114+
"io": {
115+
"low": IOPRIO_CLASS_IDLE,
116+
"normal": IOPRIO_CLASS_BE,
117+
"high": IOPRIO_CLASS_RT,
118+
},
119+
}
120+
121+
68122
try:
69123
import signal
70124
except ImportError:
@@ -75,6 +129,7 @@
75129
import queue
76130
except ImportError:
77131
import Queue as queue
132+
import threading
78133

79134
# Python 2.7 compat fixes (missing typing)
80135
try:
@@ -257,56 +312,60 @@ def wrapper(*args, **kwargs):
257312
PIPE = subprocess.PIPE
258313

259314

315+
def _check_priority_value(priority):
316+
"""
317+
Check if priority int is valid
318+
"""
319+
valid_priorities = list(PRIORITIES["process"].keys())
320+
if isinstance(priority, str):
321+
if priority not in valid_priorities:
322+
raise ValueError(
323+
"Priority not valid: {}. Please use on of {}".format(
324+
priority, ", ".join(valid_priorities)
325+
)
326+
)
327+
elif isinstance(priority, int):
328+
if os_name == "nt":
329+
raise ValueError(
330+
"Priority int not valid on Windows: {}. Please use one of {}".format(
331+
priority, ", ".join(valid_priorities)
332+
)
333+
)
334+
if -20 <= priority <= 20:
335+
raise ValueError(
336+
"Priority int not valid: {}. Please use one between -20 and 19".format(
337+
priority
338+
)
339+
)
340+
341+
260342
def _set_priority(
261343
pid, # type: int
262344
priority, # type: Union[int, str]
263345
priority_type, # type: str
264346
):
265347
"""
266-
Set process and / or io priorities
267-
Since Windows and Linux use different possible values, let's simplify things by allowing 3 prioriy types
348+
Set process and / or io prioritie
268349
"""
269350
priority = priority.lower()
270351

271352
if priority_type == "process":
272-
if isinstance(priority, int) and os_name != "nt" and -20 <= priority <= 20:
273-
raise ValueError("Bogus process priority int given: {}".format(priority))
274-
if priority not in ["low", "normal", "high"]:
275-
raise ValueError(
276-
"Bogus {} priority given: {}".format(priority_type, priority)
277-
)
278-
279-
if priority_type == "io" and priority not in ["low", "normal", "high"]:
280-
raise ValueError("Bogus {} priority given: {}".format(priority_type, priority))
281-
282-
if os_name == "nt":
283-
priorities = {
284-
"process": {
285-
"low": BELOW_NORMAL_PRIORITY_CLASS,
286-
"normal": NORMAL_PRIORITY_CLASS,
287-
"high": HIGH_PRIORITY_CLASS,
288-
},
289-
"io": {"low": IOPRIO_LOW, "normal": IOPRIO_NORMAL, "high": IOPRIO_HIGH},
290-
}
291-
else:
292-
priorities = {
293-
"process": {"low": 15, "normal": 0, "high": -15},
294-
"io": {
295-
"low": IOPRIO_CLASS_IDLE,
296-
"normal": IOPRIO_CLASS_BE,
297-
"high": IOPRIO_CLASS_RT,
298-
},
299-
}
300-
301-
if priority_type == "process":
353+
_check_priority_value(priority)
302354
# Allow direct priority nice settings under linux
303355
if isinstance(priority, int):
304356
_priority = priority
305357
else:
306-
_priority = priorities[priority_type][priority]
358+
_priority = PRIORITIES[priority_type][priority]
307359
psutil.Process(pid).nice(_priority)
308360
elif priority_type == "io":
309-
psutil.Process(pid).ionice(priorities[priority_type][priority])
361+
valid_io_priorities = list(PRIORITIES["io"].keys())
362+
if priority not in valid_io_priorities:
363+
raise ValueError(
364+
"Bogus {} priority given: {}. Please use one of {}".format(
365+
priority_type, priority, ", ".join(valid_io_priorities)
366+
)
367+
)
368+
psutil.Process(pid).ionice(PRIORITIES[priority_type][priority])
310369
else:
311370
raise ValueError("Bogus priority type given.")
312371

@@ -961,9 +1020,21 @@ def _monitor_process(
9611020

9621021
# Python >= 3.3 has SubProcessError(TimeoutExpired) class
9631022
# Python >= 3.6 has encoding & error arguments
1023+
# Python >= 3.7 has creationflags arguments for process priority under windows
9641024
# universal_newlines=True makes netstat command fail under windows
9651025
# timeout does not work under Python 2.7 with subprocess32 < 3.5
9661026
# decoder may be cp437 or unicode_escape for dos commands or utf-8 for powershell
1027+
1028+
if priority:
1029+
_check_priority_value(priority)
1030+
process_prio = PRIORITIES["process"][priority.lower()]
1031+
# Don't bother to make pylint go crazy on Windows missing os.nice()
1032+
# pylint: disable=E1101
1033+
if os_name == "nt" and sys.version_info >= (3, 7):
1034+
creationflags |= process_prio
1035+
else:
1036+
kwargs["preexec_fn"] = lambda: os.nice(process_prio)
1037+
9671038
# Disabling pylint error for the same reason as above
9681039
# pylint: disable=E1123
9691040
if sys.version_info >= (3, 6):
@@ -995,8 +1066,8 @@ def _monitor_process(
9951066
**kwargs
9961067
)
9971068

998-
# Set process priority if given
999-
if priority:
1069+
# Set process priority if not set earlier by creationflags or preexec_fn
1070+
if priority and sys.version_info < (3, 7) and os_name == "nt":
10001071
try:
10011072
try:
10021073
set_priority(process.pid, priority)

0 commit comments

Comments
 (0)