|
21 | 21 | __author__ = "Orsiris de Jong" |
22 | 22 | __copyright__ = "Copyright (C) 2015-2025 Orsiris de Jong for NetInvent" |
23 | 23 | __licence__ = "BSD 3 Clause" |
24 | | -__version__ = "1.7.2" |
25 | | -__build__ = "2025031001" |
| 24 | +__version__ = "1.7.3-dev" |
| 25 | +__build__ = "2025031401" |
26 | 26 | __compat__ = "python2.7+" |
27 | 27 |
|
28 | 28 | import io |
|
33 | 33 | from datetime import datetime |
34 | 34 | from logging import getLogger |
35 | 35 | from time import sleep |
36 | | -import threading |
37 | 36 |
|
38 | 37 |
|
39 | 38 | # Avoid checking os type numerous times |
40 | 39 | os_name = os.name |
41 | 40 |
|
| 41 | + |
| 42 | +# Don't bother with an ImportError since we need command_runner to work without dependencies |
42 | 43 | try: |
43 | 44 | 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 | + |
48 | 46 | # Also make sure we directly import priority classes so we can reuse them |
49 | 47 | if os_name == "nt": |
50 | 48 | from psutil import ( |
51 | | - ABOVE_NORMAL_PRIORITY_CLASS, |
| 49 | + # ABOVE_NORMAL_PRIORITY_CLASS, |
52 | 50 | BELOW_NORMAL_PRIORITY_CLASS, |
53 | 51 | HIGH_PRIORITY_CLASS, |
54 | 52 | IDLE_PRIORITY_CLASS, |
55 | 53 | NORMAL_PRIORITY_CLASS, |
56 | 54 | REALTIME_PRIORITY_CLASS, |
57 | 55 | ) |
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 | + ) |
59 | 62 | else: |
60 | 63 | from psutil import ( |
61 | 64 | IOPRIO_CLASS_BE, |
62 | 65 | IOPRIO_CLASS_IDLE, |
63 | | - IOPRIO_CLASS_NONE, |
| 66 | + # IOPRIO_CLASS_NONE, |
64 | 67 | IOPRIO_CLASS_RT, |
65 | 68 | ) |
66 | 69 | 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 | + |
68 | 122 | try: |
69 | 123 | import signal |
70 | 124 | except ImportError: |
|
75 | 129 | import queue |
76 | 130 | except ImportError: |
77 | 131 | import Queue as queue |
| 132 | +import threading |
78 | 133 |
|
79 | 134 | # Python 2.7 compat fixes (missing typing) |
80 | 135 | try: |
@@ -257,56 +312,60 @@ def wrapper(*args, **kwargs): |
257 | 312 | PIPE = subprocess.PIPE |
258 | 313 |
|
259 | 314 |
|
| 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 | + |
260 | 342 | def _set_priority( |
261 | 343 | pid, # type: int |
262 | 344 | priority, # type: Union[int, str] |
263 | 345 | priority_type, # type: str |
264 | 346 | ): |
265 | 347 | """ |
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 |
268 | 349 | """ |
269 | 350 | priority = priority.lower() |
270 | 351 |
|
271 | 352 | 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) |
302 | 354 | # Allow direct priority nice settings under linux |
303 | 355 | if isinstance(priority, int): |
304 | 356 | _priority = priority |
305 | 357 | else: |
306 | | - _priority = priorities[priority_type][priority] |
| 358 | + _priority = PRIORITIES[priority_type][priority] |
307 | 359 | psutil.Process(pid).nice(_priority) |
308 | 360 | 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]) |
310 | 369 | else: |
311 | 370 | raise ValueError("Bogus priority type given.") |
312 | 371 |
|
@@ -961,9 +1020,21 @@ def _monitor_process( |
961 | 1020 |
|
962 | 1021 | # Python >= 3.3 has SubProcessError(TimeoutExpired) class |
963 | 1022 | # Python >= 3.6 has encoding & error arguments |
| 1023 | + # Python >= 3.7 has creationflags arguments for process priority under windows |
964 | 1024 | # universal_newlines=True makes netstat command fail under windows |
965 | 1025 | # timeout does not work under Python 2.7 with subprocess32 < 3.5 |
966 | 1026 | # 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 | + |
967 | 1038 | # Disabling pylint error for the same reason as above |
968 | 1039 | # pylint: disable=E1123 |
969 | 1040 | if sys.version_info >= (3, 6): |
@@ -995,8 +1066,8 @@ def _monitor_process( |
995 | 1066 | **kwargs |
996 | 1067 | ) |
997 | 1068 |
|
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": |
1000 | 1071 | try: |
1001 | 1072 | try: |
1002 | 1073 | set_priority(process.pid, priority) |
|
0 commit comments