Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,8 @@ Process class
the returned value is *not* split evenly between all available CPUs
(differently from :func:`psutil.cpu_percent`). To emulate Windows
``taskmgr.exe`` behavior: ``p.cpu_percent() / psutil.cpu_count()``.
:meth:`oneshot` does not cache these samples; each call still reads live
CPU times.

.. seealso::
- :ref:`faq_cpu_percent`
Expand Down
31 changes: 27 additions & 4 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ def oneshot(self) -> Generator[None, None, None]:
>>> with p.oneshot():
... p.name() # collect multiple info
... p.cpu_times() # return cached value
... p.cpu_percent() # return cached value
... p.cpu_percent() # still samples live times
... p.create_time() # return cached value
...
>>>
Expand Down Expand Up @@ -1244,15 +1244,15 @@ def timer():

if blocking:
st1 = timer()
pt1 = self._proc.cpu_times()
pt1 = self._cpu_times_now()
time.sleep(interval)
st2 = timer()
pt2 = self._proc.cpu_times()
pt2 = self._cpu_times_now()
else:
st1 = self._last_sys_cpu_times
pt1 = self._last_proc_cpu_times
st2 = timer()
pt2 = self._proc.cpu_times()
pt2 = self._cpu_times_now()
if st1 is None or pt1 is None:
self._last_sys_cpu_times = st2
self._last_proc_cpu_times = pt2
Expand Down Expand Up @@ -1291,6 +1291,29 @@ def timer():
single_cpu_percent = overall_cpus_percent * num_cpus
return round(single_cpu_percent, 1)

def _cpu_times_now(self):
"""Read cpu_times() ignoring the oneshot() cache.

cpu_percent compares two samples. oneshot() would otherwise
freeze /proc/stat (and the equivalent on other platforms) so
both samples are identical and the method always returns 0.0.
"""
proc = self._proc
cache = getattr(proc, "_cache", None)
saved = dict(cache) if cache is not None else None
if cache is not None:
cache.clear()
try:
return proc.cpu_times()
finally:
if saved is not None:
restored = getattr(proc, "_cache", None)
if restored is None:
proc._cache = saved
else:
restored.clear()
restored.update(saved)

@_use_prefetch
@memoize_when_activated
def cpu_times(self) -> pcputimes:
Expand Down
9 changes: 9 additions & 0 deletions tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ def test_cpu_percent_numcpus_none(self):
psutil.Process().cpu_percent()
assert m.called

@retry_on_failure
def test_cpu_percent_oneshot(self):
# oneshot() caches cpu_times(); cpu_percent still needs a fresh
# sample, see https://github.com/giampaolo/psutil/issues/2072
p = self.spawn_psproc([PYTHON_EXE, "-c", "while True: pass"])
with p.oneshot():
percent = p.cpu_percent(interval=0.2)
assert percent > 0.0

def test_cpu_times(self):
times = psutil.Process().cpu_times()
assert times.user >= 0.0, times
Expand Down
Loading