summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2018-03-17 13:00:46 +0100
committerGitHub <noreply@github.com>2018-03-17 13:00:46 +0100
commit81ef4c9185650213c03122c95856ad3991b5656d (patch)
treec6087f5a57c3caef4f997d77f145482eeba386eb
parentfd78d32681c78dd81742dae6bb7b4b0da3d18010 (diff)
downloadpsutil-81ef4c9185650213c03122c95856ad3991b5656d.tar.gz
refactor open mock + add purge script (#1249)
* update doc / HISTORY / CREDITS * suppress warnings from mock module * linux: add mock test for virtual_memory() * add a script to purge installation * linux: disable unreliable test * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * fix typo * fix typo * refactor open mock * refactor open mock * refactor open mock * refactor open mock * refactor open mock * add docstrings
-rw-r--r--CREDITS4
-rw-r--r--HISTORY.rst5
-rw-r--r--MANIFEST.in1
-rw-r--r--Makefile3
-rw-r--r--docs/index.rst8
-rw-r--r--psutil/tests/__init__.py4
-rwxr-xr-xpsutil/tests/test_linux.py772
-rwxr-xr-xscripts/internal/purge.py42
8 files changed, 421 insertions, 418 deletions
diff --git a/CREDITS b/CREDITS
index f0327a7b..ae384a53 100644
--- a/CREDITS
+++ b/CREDITS
@@ -527,3 +527,7 @@ I: 1243
N: Georg Sauthoff
W: https://github.com/gsauthof
I: 1193, 1194
+
+N: Maxime Mouial
+W: https://github.com/hush-hush
+I: 1239
diff --git a/HISTORY.rst b/HISTORY.rst
index 078ebff2..06cb3eee 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -5,6 +5,11 @@
XXXX-XX-XX
+**Enhancements**
+
+- 1239_: [Linux] expose kernel "slab" memory for psutil.virtual_memory().
+ (patch by Maxime Mouial)
+
**Bug fixes**
- 694_: [SunOS] cmdline() could be truncated at the 15th character when
diff --git a/MANIFEST.in b/MANIFEST.in
index 7a92a4e5..87d9bebb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -110,6 +110,7 @@ include scripts/internal/download_exes.py
include scripts/internal/generate_manifest.py
include scripts/internal/print_announce.py
include scripts/internal/print_timeline.py
+include scripts/internal/purge.py
include scripts/internal/winmake.py
include scripts/iotop.py
include scripts/killall.py
diff --git a/Makefile b/Makefile
index 5081a4ed..d9f4a854 100644
--- a/Makefile
+++ b/Makefile
@@ -76,7 +76,8 @@ install: ## Install this package as current user in "edit" mode.
rm -rf tmp
uninstall: ## Uninstall this package via pip.
- cd ..; $(PYTHON) -m pip uninstall -y -v psutil
+ cd ..; $(PYTHON) -m pip uninstall -y -v psutil || true
+ $(PYTHON) scripts/internal/purge.py
install-pip: ## Install pip (no-op if already installed).
$(PYTHON) -c \
diff --git a/docs/index.rst b/docs/index.rst
index 0f55a2e1..395fd688 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -266,11 +266,11 @@ Memory
- **active** *(UNIX)*: memory currently in use or very recently used, and so
it is in RAM.
- **inactive** *(UNIX)*: memory that is marked as not used.
- - **slab** *(Linux)*: in-kernel data structures cache.
- **buffers** *(Linux, BSD)*: cache for things like file system metadata.
- **cached** *(Linux, BSD)*: cache for various things.
- **shared** *(Linux, BSD)*: memory that may be simultaneously accessed by
multiple processes.
+ - **slab** *(Linux)*: in-kernel data structures cache.
- **wired** *(BSD, OSX)*: memory that is marked to always stay in RAM. It is
never moved to disk.
@@ -293,11 +293,9 @@ Memory
...
>>>
- .. versionchanged:: 4.2.0 added *shared* metrics on Linux.
-
- .. versionchanged:: 4.4.0 *available* and *used* values on Linux are more
- precise and match "free" cmdline utility.
+ .. versionchanged:: 4.2.0 added *shared* metric on Linux.
+ .. versionchanged:: 5.4.4 added *slab* metric on Linux.
.. function:: swap_memory()
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 499240fb..7be6fdc6 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -54,7 +54,9 @@ else:
try:
from unittest import mock # py3
except ImportError:
- import mock # NOQA - requires "pip install mock"
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ import mock # NOQA - requires "pip install mock"
if sys.version_info >= (3, 4):
import enum
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 13303a54..dc905796 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -13,7 +13,6 @@ import errno
import glob
import io
import os
-import pprint
import re
import shutil
import socket
@@ -25,6 +24,7 @@ import warnings
import psutil
from psutil import LINUX
+from psutil._compat import basestring
from psutil._compat import PY3
from psutil._compat import u
from psutil.tests import call_until
@@ -143,6 +143,46 @@ def get_free_version_info():
return tuple(map(int, out.split()[-1].split('.')))
+@contextlib.contextmanager
+def mock_open_content(for_path, content):
+ """Mock open() builtin and forces it to return a certain `content`
+ on read() if the path being opened matches `for_path`.
+ """
+ def open_mock(name, *args, **kwargs):
+ if name == for_path:
+ if PY3:
+ if isinstance(content, basestring):
+ return io.StringIO(content)
+ else:
+ return io.BytesIO(content)
+ else:
+ return io.BytesIO(content)
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ yield m
+
+
+@contextlib.contextmanager
+def mock_open_exception(for_path, exc):
+ """Mock open() builtin and raises `exc` if the path being opened
+ matches `for_path`.
+ """
+ def open_mock(name, *args, **kwargs):
+ if name == for_path:
+ raise exc
+ else:
+ return orig_open(name, *args, **kwargs)
+
+ orig_open = open
+ patch_point = 'builtins.open' if PY3 else '__builtin__.open'
+ with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ yield m
+
+
# =====================================================================
# --- system virtual memory
# =====================================================================
@@ -237,53 +277,22 @@ class TestSystemVirtualMemory(unittest.TestCase):
free_value, psutil_value, delta=MEMORY_TOLERANCE,
msg='%s %s \n%s' % (free_value, psutil_value, out))
- def test_slab(self):
- # Emulate /proc/meminfo because neither vmstat nor free return slab.
- def open_mock(name, *args, **kwargs):
- if name == '/proc/meminfo':
- return io.BytesIO(textwrap.dedent("""\
- Active(anon): 6145416 kB
- Active(file): 2950064 kB
- Inactive(anon): 574764 kB
- Inactive(file): 1567648 kB
- MemAvailable: -1 kB
- MemFree: 2057400 kB
- MemTotal: 16325648 kB
- SReclaimable: 346648 kB
- Slab: 186836 kB
- """).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
- ret = psutil.virtual_memory()
- assert m.called
- self.assertEqual(ret.slab, 191320064)
-
def test_warnings_on_misses(self):
# Emulate a case where /proc/meminfo provides few info.
# psutil is supposed to set the missing fields to 0 and
# raise a warning.
- def open_mock(name, *args, **kwargs):
- if name == '/proc/meminfo':
- return io.BytesIO(textwrap.dedent("""\
- Active(anon): 6145416 kB
- Active(file): 2950064 kB
- Inactive(anon): 574764 kB
- Inactive(file): 1567648 kB
- MemAvailable: -1 kB
- MemFree: 2057400 kB
- MemTotal: 16325648 kB
- SReclaimable: 346648 kB
- """).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content(
+ '/proc/meminfo',
+ textwrap.dedent("""\
+ Active(anon): 6145416 kB
+ Active(file): 2950064 kB
+ Inactive(anon): 574764 kB
+ Inactive(file): 1567648 kB
+ MemAvailable: -1 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ SReclaimable: 346648 kB
+ """).encode()) as m:
with warnings.catch_warnings(record=True) as ws:
warnings.simplefilter("always")
ret = psutil.virtual_memory()
@@ -328,29 +337,23 @@ class TestSystemVirtualMemory(unittest.TestCase):
def test_avail_old_comes_from_kernel(self):
# Make sure "MemAvailable:" coluimn is used instead of relying
# on our internal algorithm to calculate avail mem.
- def open_mock(name, *args, **kwargs):
- if name == "/proc/meminfo":
- return io.BytesIO(textwrap.dedent("""\
- Active: 9444728 kB
- Active(anon): 6145416 kB
- Active(file): 2950064 kB
- Buffers: 287952 kB
- Cached: 4818144 kB
- Inactive(file): 1578132 kB
- Inactive(anon): 574764 kB
- Inactive(file): 1567648 kB
- MemAvailable: 6574984 kB
- MemFree: 2057400 kB
- MemTotal: 16325648 kB
- Shmem: 577588 kB
- SReclaimable: 346648 kB
- """).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content(
+ '/proc/meminfo',
+ textwrap.dedent("""\
+ Active: 9444728 kB
+ Active(anon): 6145416 kB
+ Active(file): 2950064 kB
+ Buffers: 287952 kB
+ Cached: 4818144 kB
+ Inactive(file): 1578132 kB
+ Inactive(anon): 574764 kB
+ Inactive(file): 1567648 kB
+ MemAvailable: 6574984 kB
+ MemFree: 2057400 kB
+ MemTotal: 16325648 kB
+ Shmem: 577588 kB
+ SReclaimable: 346648 kB
+ """).encode()) as m:
with warnings.catch_warnings(record=True) as ws:
ret = psutil.virtual_memory()
assert m.called
@@ -363,9 +366,9 @@ class TestSystemVirtualMemory(unittest.TestCase):
# Remove Active(file), Inactive(file) and SReclaimable
# from /proc/meminfo and make sure the fallback is used
# (free + cached),
- def open_mock(name, *args, **kwargs):
- if name == "/proc/meminfo":
- return io.BytesIO(textwrap.dedent("""\
+ with mock_open_content(
+ "/proc/meminfo",
+ textwrap.dedent("""\
Active: 9444728 kB
Active(anon): 6145416 kB
Buffers: 287952 kB
@@ -375,13 +378,7 @@ class TestSystemVirtualMemory(unittest.TestCase):
MemFree: 2057400 kB
MemTotal: 16325648 kB
Shmem: 577588 kB
- """).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ """).encode()) as m:
with warnings.catch_warnings(record=True) as ws:
ret = psutil.virtual_memory()
assert m.called
@@ -393,9 +390,9 @@ class TestSystemVirtualMemory(unittest.TestCase):
def test_avail_old_missing_zoneinfo(self):
# Remove /proc/zoneinfo file. Make sure fallback is used
# (free + cached).
- def open_mock(name, *args, **kwargs):
- if name == "/proc/meminfo":
- return io.BytesIO(textwrap.dedent("""\
+ with mock_open_content(
+ "/proc/meminfo",
+ textwrap.dedent("""\
Active: 9444728 kB
Active(anon): 6145416 kB
Active(file): 2950064 kB
@@ -408,22 +405,85 @@ class TestSystemVirtualMemory(unittest.TestCase):
MemTotal: 16325648 kB
Shmem: 577588 kB
SReclaimable: 346648 kB
- """).encode())
- elif name == "/proc/zoneinfo":
- raise IOError(errno.ENOENT, 'no such file or directory')
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
- with warnings.catch_warnings(record=True) as ws:
- ret = psutil.virtual_memory()
+ """).encode()):
+ with mock_open_exception(
+ "/proc/zoneinfo",
+ IOError(errno.ENOENT, 'no such file or directory')):
+ with warnings.catch_warnings(record=True) as ws:
+ ret = psutil.virtual_memory()
+ self.assertEqual(
+ ret.available, 2057400 * 1024 + 4818144 * 1024)
+ w = ws[0]
+ self.assertIn(
+ "inactive memory stats couldn't be determined",
+ str(w.message))
+
+ def test_virtual_memory_mocked(self):
+ # Emulate /proc/meminfo because neither vmstat nor free return slab.
+ with mock_open_content(
+ '/proc/meminfo',
+ textwrap.dedent("""\
+ MemTotal: 100 kB
+ MemFree: 2 kB
+ MemAvailable: 3 kB
+ Buffers: 4 kB
+ Cached: 5 kB
+ SwapCached: 6 kB
+ Active: 7 kB
+ Inactive: 8 kB
+ Active(anon): 9 kB
+ Inactive(anon): 10 kB
+ Active(file): 11 kB
+ Inactive(file): 12 kB
+ Unevictable: 13 kB
+ Mlocked: 14 kB
+ SwapTotal: 15 kB
+ SwapFree: 16 kB
+ Dirty: 17 kB
+ Writeback: 18 kB
+ AnonPages: 19 kB
+ Mapped: 20 kB
+ Shmem: 21 kB
+ Slab: 22 kB
+ SReclaimable: 23 kB
+ SUnreclaim: 24 kB
+ KernelStack: 25 kB
+ PageTables: 26 kB
+ NFS_Unstable: 27 kB
+ Bounce: 28 kB
+ WritebackTmp: 29 kB
+ CommitLimit: 30 kB
+ Committed_AS: 31 kB
+ VmallocTotal: 32 kB
+ VmallocUsed: 33 kB
+ VmallocChunk: 34 kB
+ HardwareCorrupted: 35 kB
+ AnonHugePages: 36 kB
+ ShmemHugePages: 37 kB
+ ShmemPmdMapped: 38 kB
+ CmaTotal: 39 kB
+ CmaFree: 40 kB
+ HugePages_Total: 41 kB
+ HugePages_Free: 42 kB
+ HugePages_Rsvd: 43 kB
+ HugePages_Surp: 44 kB
+ Hugepagesize: 45 kB
+ DirectMap46k: 46 kB
+ DirectMap47M: 47 kB
+ DirectMap48G: 48 kB
+ """).encode()) as m:
+ mem = psutil.virtual_memory()
assert m.called
- self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024)
- w = ws[0]
- self.assertIn(
- "inactive memory stats couldn't be determined", str(w.message))
+ self.assertEqual(mem.total, 100 * 1024)
+ self.assertEqual(mem.free, 2 * 1024)
+ self.assertEqual(mem.buffers, 4 * 1024)
+ # cached mem also includes reclaimable memory
+ self.assertEqual(mem.cached, (5 + 23) * 1024)
+ self.assertEqual(mem.shared, 21 * 1024)
+ self.assertEqual(mem.active, 7 * 1024)
+ self.assertEqual(mem.inactive, 8 * 1024)
+ self.assertEqual(mem.slab, 22 * 1024)
+ self.assertEqual(mem.available, 3 * 1024)
# =====================================================================
@@ -478,15 +538,9 @@ class TestSystemSwapMemory(unittest.TestCase):
def test_no_vmstat_mocked(self):
# see https://github.com/giampaolo/psutil/issues/722
- def open_mock(name, *args, **kwargs):
- if name == "/proc/vmstat":
- raise IOError(errno.ENOENT, 'no such file or directory')
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_exception(
+ "/proc/vmstat",
+ IOError(errno.ENOENT, 'no such file or directory')) as m:
with warnings.catch_warnings(record=True) as ws:
warnings.simplefilter("always")
ret = psutil.swap_memory()
@@ -521,15 +575,7 @@ class TestSystemSwapMemory(unittest.TestCase):
# Emulate a case where /proc/meminfo provides no swap metrics
# in which case sysinfo() syscall is supposed to be used
# as a fallback.
- def open_mock(name, *args, **kwargs):
- if name == "/proc/meminfo":
- return io.BytesIO(b"")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content("/proc/meminfo", b"") as m:
psutil.swap_memory()
assert m.called
@@ -616,16 +662,9 @@ class TestSystemCPU(unittest.TestCase):
# Finally, let's make /proc/cpuinfo return meaningless data;
# this way we'll fall back on relying on /proc/stat
- def open_mock(name, *args, **kwargs):
- if name.startswith('/proc/cpuinfo'):
- return io.BytesIO(b"")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock, create=True):
+ with mock_open_content('/proc/cpuinfo', b"") as m:
self.assertEqual(psutil._pslinux.cpu_count_logical(), original)
+ m.called
def test_cpu_count_physical_mocked(self):
# Have open() return emtpy data and make sure None is returned
@@ -838,20 +877,21 @@ class TestSystemNetwork(unittest.TestCase):
self.assertAlmostEqual(
stats.dropout, ifconfig_ret['dropout'], delta=10)
- @unittest.skipIf(not which('ip'), "'ip' utility not available")
- @unittest.skipIf(TRAVIS, "skipped on Travis")
- def test_net_if_names(self):
- out = sh("ip addr").strip()
- nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x]
- found = 0
- for line in out.split('\n'):
- line = line.strip()
- if re.search(r"^\d+:", line):
- found += 1
- name = line.split(':')[1].strip()
- self.assertIn(name, nics)
- self.assertEqual(len(nics), found, msg="%s\n---\n%s" % (
- pprint.pformat(nics), out))
+ # XXX - not reliable when having virtual NICs installed by Docker.
+ # @unittest.skipIf(not which('ip'), "'ip' utility not available")
+ # @unittest.skipIf(TRAVIS, "skipped on Travis")
+ # def test_net_if_names(self):
+ # out = sh("ip addr").strip()
+ # nics = [x for x in psutil.net_if_addrs().keys() if ':' not in x]
+ # found = 0
+ # for line in out.split('\n'):
+ # line = line.strip()
+ # if re.search(r"^\d+:", line):
+ # found += 1
+ # name = line.split(':')[1].strip()
+ # self.assertIn(name, nics)
+ # self.assertEqual(len(nics), found, msg="%s\n---\n%s" % (
+ # pprint.pformat(nics), out))
@mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError)
@mock.patch('psutil._pslinux.supports_ipv6', return_value=False)
@@ -866,20 +906,14 @@ class TestSystemNetwork(unittest.TestCase):
psutil.net_connections(kind='inet6')
def test_net_connections_mocked(self):
- def open_mock(name, *args, **kwargs):
- if name == '/proc/net/unix':
- return io.StringIO(textwrap.dedent(u"""\
- 0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n
- 0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ
- 0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O
- 000000000000000000000000000000000000000000000000000000
- """))
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
+ with mock_open_content(
+ '/proc/net/unix',
+ textwrap.dedent("""\
+ 0: 00000003 000 000 0001 03 462170 @/tmp/dbus-Qw2hMPIU3n
+ 0: 00000003 000 000 0001 03 35010 @/tmp/dbus-tB2X8h69BQ
+ 0: 00000003 000 000 0001 03 34424 @/tmp/dbus-cHy80Y8O
+ 000000000000000000000000000000000000000000000000000000
+ """)) as m:
psutil.net_connections(kind='unix')
assert m.called
@@ -945,65 +979,51 @@ class TestSystemDisks(unittest.TestCase):
def test_disk_io_counters_kernel_2_4_mocked(self):
# Tests /proc/diskstats parsing format for 2.4 kernels, see:
# https://github.com/giampaolo/psutil/issues/767
- def open_mock(name, *args, **kwargs):
- if name == '/proc/partitions':
- return io.StringIO(textwrap.dedent(u"""\
+ with mock_open_content(
+ '/proc/partitions',
+ textwrap.dedent("""\
major minor #blocks name
8 0 488386584 hda
- """))
- elif name == '/proc/diskstats':
- return io.StringIO(
- u(" 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12"))
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- ret = psutil.disk_io_counters(nowrap=False)
- assert m.called
- self.assertEqual(ret.read_count, 1)
- self.assertEqual(ret.read_merged_count, 2)
- self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE)
- self.assertEqual(ret.read_time, 4)
- self.assertEqual(ret.write_count, 5)
- self.assertEqual(ret.write_merged_count, 6)
- self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE)
- self.assertEqual(ret.write_time, 8)
- self.assertEqual(ret.busy_time, 10)
+ """)):
+ with mock_open_content(
+ '/proc/diskstats',
+ " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12"):
+ ret = psutil.disk_io_counters(nowrap=False)
+ self.assertEqual(ret.read_count, 1)
+ self.assertEqual(ret.read_merged_count, 2)
+ self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE)
+ self.assertEqual(ret.read_time, 4)
+ self.assertEqual(ret.write_count, 5)
+ self.assertEqual(ret.write_merged_count, 6)
+ self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE)
+ self.assertEqual(ret.write_time, 8)
+ self.assertEqual(ret.busy_time, 10)
def test_disk_io_counters_kernel_2_6_full_mocked(self):
# Tests /proc/diskstats parsing format for 2.6 kernels,
# lines reporting all metrics:
# https://github.com/giampaolo/psutil/issues/767
- def open_mock(name, *args, **kwargs):
- if name == '/proc/partitions':
- return io.StringIO(textwrap.dedent(u"""\
+ with mock_open_content(
+ '/proc/partitions',
+ textwrap.dedent("""\
major minor #blocks name
8 0 488386584 hda
- """))
- elif name == '/proc/diskstats':
- return io.StringIO(
- u(" 3 0 hda 1 2 3 4 5 6 7 8 9 10 11"))
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- ret = psutil.disk_io_counters(nowrap=False)
- assert m.called
- self.assertEqual(ret.read_count, 1)
- self.assertEqual(ret.read_merged_count, 2)
- self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE)
- self.assertEqual(ret.read_time, 4)
- self.assertEqual(ret.write_count, 5)
- self.assertEqual(ret.write_merged_count, 6)
- self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE)
- self.assertEqual(ret.write_time, 8)
- self.assertEqual(ret.busy_time, 10)
+ """)):
+ with mock_open_content(
+ '/proc/diskstats',
+ " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11"):
+ ret = psutil.disk_io_counters(nowrap=False)
+ self.assertEqual(ret.read_count, 1)
+ self.assertEqual(ret.read_merged_count, 2)
+ self.assertEqual(ret.read_bytes, 3 * SECTOR_SIZE)
+ self.assertEqual(ret.read_time, 4)
+ self.assertEqual(ret.write_count, 5)
+ self.assertEqual(ret.write_merged_count, 6)
+ self.assertEqual(ret.write_bytes, 7 * SECTOR_SIZE)
+ self.assertEqual(ret.write_time, 8)
+ self.assertEqual(ret.busy_time, 10)
def test_disk_io_counters_kernel_2_6_limited_mocked(self):
# Tests /proc/diskstats parsing format for 2.6 kernels,
@@ -1011,34 +1031,27 @@ class TestSystemDisks(unittest.TestCase):
# amount of metrics when it bumps into a partition
# (instead of a disk). See:
# https://github.com/giampaolo/psutil/issues/767
- def open_mock(name, *args, **kwargs):
- if name == '/proc/partitions':
- return io.StringIO(textwrap.dedent(u"""\
+ with mock_open_content(
+ '/proc/partitions',
+ textwrap.dedent("""\
major minor #blocks name
8 0 488386584 hda
- """))
- elif name == '/proc/diskstats':
- return io.StringIO(
- u(" 3 1 hda 1 2 3 4"))
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- ret = psutil.disk_io_counters(nowrap=False)
- assert m.called
- self.assertEqual(ret.read_count, 1)
- self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE)
- self.assertEqual(ret.write_count, 3)
- self.assertEqual(ret.write_bytes, 4 * SECTOR_SIZE)
-
- self.assertEqual(ret.read_merged_count, 0)
- self.assertEqual(ret.read_time, 0)
- self.assertEqual(ret.write_merged_count, 0)
- self.assertEqual(ret.write_time, 0)
- self.assertEqual(ret.busy_time, 0)
+ """)):
+ with mock_open_content(
+ '/proc/diskstats',
+ " 3 1 hda 1 2 3 4"):
+ ret = psutil.disk_io_counters(nowrap=False)
+ self.assertEqual(ret.read_count, 1)
+ self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE)
+ self.assertEqual(ret.write_count, 3)
+ self.assertEqual(ret.write_bytes, 4 * SECTOR_SIZE)
+
+ self.assertEqual(ret.read_merged_count, 0)
+ self.assertEqual(ret.read_time, 0)
+ self.assertEqual(ret.write_merged_count, 0)
+ self.assertEqual(ret.write_time, 0)
+ self.assertEqual(ret.busy_time, 0)
# =====================================================================
@@ -1119,20 +1132,13 @@ class TestMisc(unittest.TestCase):
def test_cpu_steal_decrease(self):
# Test cumulative cpu stats decrease. We should ignore this.
# See issue #1210.
-
- def open_mock(name, *args, **kwargs):
- if name == "/proc/stat":
- return io.BytesIO(textwrap.dedent("""\
- cpu 0 0 0 0 0 0 0 1 0 0
- cpu0 0 0 0 0 0 0 0 1 0 0
- cpu1 0 0 0 0 0 0 0 1 0 0
- """).encode())
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
-
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content(
+ "/proc/stat",
+ textwrap.dedent("""\
+ cpu 0 0 0 0 0 0 0 1 0 0
+ cpu0 0 0 0 0 0 0 0 1 0 0
+ cpu1 0 0 0 0 0 0 0 1 0 0
+ """).encode()) as m:
# first call to "percent" functions should read the new stat file
# and compare to the "real" file read at import time - so the
# values are meaningless
@@ -1142,16 +1148,13 @@ class TestMisc(unittest.TestCase):
psutil.cpu_times_percent()
psutil.cpu_times_percent(percpu=True)
- def open_mock(name, *args, **kwargs):
- if name == "/proc/stat":
- return io.BytesIO(textwrap.dedent("""\
- cpu 1 0 0 0 0 0 0 0 0 0
- cpu0 1 0 0 0 0 0 0 0 0 0
- cpu1 1 0 0 0 0 0 0 0 0 0
- """).encode())
- return orig_open(name, *args, **kwargs)
-
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content(
+ "/proc/stat",
+ textwrap.dedent("""\
+ cpu 1 0 0 0 0 0 0 0 0 0
+ cpu0 1 0 0 0 0 0 0 0 0 0
+ cpu1 1 0 0 0 0 0 0 0 0 0
+ """).encode()) as m:
# Increase "user" while steal goes "backwards" to zero.
cpu_percent = psutil.cpu_percent()
assert m.called
@@ -1254,16 +1257,9 @@ class TestMisc(unittest.TestCase):
# Internally pid_exists relies on /proc/{pid}/status.
# Emulate a case where this file is empty in which case
# psutil is supposed to fall back on using pids().
- def open_mock(name, *args, **kwargs):
- if name == "/proc/%s/status" % os.getpid():
- return io.StringIO(u(""))
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock):
+ with mock_open_content("/proc/%s/status", "") as m:
assert psutil.pid_exists(os.getpid())
+ assert m.called
# =====================================================================
@@ -1378,51 +1374,33 @@ class TestSensorsBattery(unittest.TestCase):
def test_emulate_no_base_files(self):
# Emulate a case where base metrics files are not present,
# in which case we're supposed to get None.
- def open_mock(name, *args, **kwargs):
- if name.startswith("/sys/class/power_supply/BAT0/energy_now") or \
- name.startswith("/sys/class/power_supply/BAT0/charge_now"):
- raise IOError(errno.ENOENT, "")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- self.assertIsNone(psutil.sensors_battery())
- assert m.called
+ with mock_open_exception(
+ "/sys/class/power_supply/BAT0/energy_now",
+ IOError(errno.ENOENT, "")):
+ with mock_open_exception(
+ "/sys/class/power_supply/BAT0/charge_now",
+ IOError(errno.ENOENT, "")):
+ self.assertIsNone(psutil.sensors_battery())
def test_emulate_energy_full_0(self):
# Emulate a case where energy_full files returns 0.
- def open_mock(name, *args, **kwargs):
- if name.startswith("/sys/class/power_supply/BAT0/energy_full"):
- return io.BytesIO(b"0")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
+ with mock_open_content(
+ "/sys/class/power_supply/BAT0/energy_full", b"0") as m:
self.assertEqual(psutil.sensors_battery().percent, 0)
assert m.called
def test_emulate_energy_full_not_avail(self):
# Emulate a case where energy_full file does not exist.
# Expected fallback on /capacity.
- def open_mock(name, *args, **kwargs):
- energy_full = "/sys/class/power_supply/BAT0/energy_full"
- charge_full = "/sys/class/power_supply/BAT0/charge_full"
- if name.startswith(energy_full) or name.startswith(charge_full):
- raise IOError(errno.ENOENT, "")
- elif name.startswith("/sys/class/power_supply/BAT0/capacity"):
- return io.BytesIO(b"88")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- self.assertEqual(psutil.sensors_battery().percent, 88)
- assert m.called
+ with mock_open_exception(
+ "/sys/class/power_supply/BAT0/energy_full",
+ IOError(errno.ENOENT, "")):
+ with mock_open_exception(
+ "/sys/class/power_supply/BAT0/charge_full",
+ IOError(errno.ENOENT, "")):
+ with mock_open_content(
+ "/sys/class/power_supply/BAT0/capacity", b"88"):
+ self.assertEqual(psutil.sensors_battery().percent, 88)
def test_emulate_no_ac0_online(self):
# Emulate a case where /AC0/online file does not exist.
@@ -1440,19 +1418,16 @@ class TestSensorsBattery(unittest.TestCase):
def test_emulate_no_power(self):
# Emulate a case where /AC0/online file nor /BAT0/status exist.
- def open_mock(name, *args, **kwargs):
- if name.startswith("/sys/class/power_supply/AC/online") or \
- name.startswith("/sys/class/power_supply/AC0/online") or \
- name.startswith("/sys/class/power_supply/BAT0/status"):
- raise IOError(errno.ENOENT, "")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
- self.assertIsNone(psutil.sensors_battery().power_plugged)
- assert m.called
+ with mock_open_exception(
+ "/sys/class/power_supply/AC/online",
+ IOError(errno.ENOENT, "")):
+ with mock_open_exception(
+ "/sys/class/power_supply/AC0/online",
+ IOError(errno.ENOENT, "")):
+ with mock_open_exception(
+ "/sys/class/power_supply/BAT0/status",
+ IOError(errno.ENOENT, "")):
+ self.assertIsNone(psutil.sensors_battery().power_plugged)
@unittest.skipIf(not LINUX, "LINUX only")
@@ -1561,37 +1536,31 @@ class TestProcess(unittest.TestCase):
def test_memory_full_info_mocked(self):
# See: https://github.com/giampaolo/psutil/issues/1222
- def open_mock(name, *args, **kwargs):
- if name == "/proc/%s/smaps" % os.getpid():
- return io.BytesIO(textwrap.dedent("""\
- fffff0 r-xp 00000000 00:00 0 [vsyscall]
- Size: 1 kB
- Rss: 2 kB
- Pss: 3 kB
- Shared_Clean: 4 kB
- Shared_Dirty: 5 kB
- Private_Clean: 6 kB
- Private_Dirty: 7 kB
- Referenced: 8 kB
- Anonymous: 9 kB
- LazyFree: 10 kB
- AnonHugePages: 11 kB
- ShmemPmdMapped: 12 kB
- Shared_Hugetlb: 13 kB
- Private_Hugetlb: 14 kB
- Swap: 15 kB
- SwapPss: 16 kB
- KernelPageSize: 17 kB
- MMUPageSize: 18 kB
- Locked: 19 kB
- VmFlags: rd ex
- """).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, create=True, side_effect=open_mock) as m:
+ with mock_open_content(
+ "/proc/%s/smaps" % os.getpid(),
+ textwrap.dedent("""\
+ fffff0 r-xp 00000000 00:00 0 [vsyscall]
+ Size: 1 kB
+ Rss: 2 kB
+ Pss: 3 kB
+ Shared_Clean: 4 kB
+ Shared_Dirty: 5 kB
+ Private_Clean: 6 kB
+ Private_Dirty: 7 kB
+ Referenced: 8 kB
+ Anonymous: 9 kB
+ LazyFree: 10 kB
+ AnonHugePages: 11 kB
+ ShmemPmdMapped: 12 kB
+ Shared_Hugetlb: 13 kB
+ Private_Hugetlb: 14 kB
+ Swap: 15 kB
+ SwapPss: 16 kB
+ KernelPageSize: 17 kB
+ MMUPageSize: 18 kB
+ Locked: 19 kB
+ VmFlags: rd ex
+ """).encode()) as m:
p = psutil.Process()
mem = p.memory_full_info()
assert m.called
@@ -1775,15 +1744,9 @@ class TestProcess(unittest.TestCase):
def test_issue_1014(self):
# Emulates a case where smaps file does not exist. In this case
# wrap_exception decorator should not raise NoSuchProcess.
- def open_mock(name, *args, **kwargs):
- if name.startswith('/proc/%s/smaps' % os.getpid()):
- raise IOError(errno.ENOENT, "")
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock) as m:
+ with mock_open_exception(
+ '/proc/%s/smaps' % os.getpid(),
+ IOError(errno.ENOENT, "")) as m:
p = psutil.Process()
with self.assertRaises(IOError) as err:
p.memory_maps()
@@ -1819,56 +1782,49 @@ class TestProcess(unittest.TestCase):
def test_stat_file_parsing(self):
from psutil._pslinux import CLOCK_TICKS
- def open_mock(name, *args, **kwargs):
- if name.startswith('/proc/%s/stat' % os.getpid()):
- args = [
- "0", # pid
- "(cat)", # name
- "Z", # status
- "1", # ppid
- "0", # pgrp
- "0", # session
- "0", # tty
- "0", # tpgid
- "0", # flags
- "0", # minflt
- "0", # cminflt
- "0", # majflt
- "0", # cmajflt
- "2", # utime
- "3", # stime
- "4", # cutime
- "5", # cstime
- "0", # priority
- "0", # nice
- "0", # num_threads
- "0", # itrealvalue
- "6", # starttime
- "0", # vsize
- "0", # rss
- "0", # rsslim
- "0", # startcode
- "0", # endcode
- "0", # startstack
- "0", # kstkesp
- "0", # kstkeip
- "0", # signal
- "0", # blocked
- "0", # sigignore
- "0", # sigcatch
- "0", # wchan
- "0", # nswap
- "0", # cnswap
- "0", # exit_signal
- "6", # processor
- ]
- return io.BytesIO(" ".join(args).encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock):
+ args = [
+ "0", # pid
+ "(cat)", # name
+ "Z", # status
+ "1", # ppid
+ "0", # pgrp
+ "0", # session
+ "0", # tty
+ "0", # tpgid
+ "0", # flags
+ "0", # minflt
+ "0", # cminflt
+ "0", # majflt
+ "0", # cmajflt
+ "2", # utime
+ "3", # stime
+ "4", # cutime
+ "5", # cstime
+ "0", # priority
+ "0", # nice
+ "0", # num_threads
+ "0", # itrealvalue
+ "6", # starttime
+ "0", # vsize
+ "0", # rss
+ "0", # rsslim
+ "0", # startcode
+ "0", # endcode
+ "0", # startstack
+ "0", # kstkesp
+ "0", # kstkeip
+ "0", # signal
+ "0", # blocked
+ "0", # sigignore
+ "0", # sigcatch
+ "0", # wchan
+ "0", # nswap
+ "0", # cnswap
+ "0", # exit_signal
+ "6", # processor
+ ]
+ content = " ".join(args).encode()
+ with mock_open_content('/proc/%s/stat' % os.getpid(), content):
p = psutil.Process()
self.assertEqual(p.name(), 'cat')
self.assertEqual(p.status(), psutil.STATUS_ZOMBIE)
@@ -1883,22 +1839,16 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.cpu_num(), 6)
def test_status_file_parsing(self):
- def open_mock(name, *args, **kwargs):
- if name.startswith('/proc/%s/status' % os.getpid()):
- return io.BytesIO(textwrap.dedent("""\
- Uid:\t1000\t1001\t1002\t1003
- Gid:\t1004\t1005\t1006\t1007
- Threads:\t66
- Cpus_allowed:\tf
- Cpus_allowed_list:\t0-7
- voluntary_ctxt_switches:\t12
- nonvoluntary_ctxt_switches:\t13""").encode())
- else:
- return orig_open(name, *args, **kwargs)
-
- orig_open = open
- patch_point = 'builtins.open' if PY3 else '__builtin__.open'
- with mock.patch(patch_point, side_effect=open_mock):
+ with mock_open_content(
+ '/proc/%s/status' % os.getpid(),
+ textwrap.dedent("""\
+ Uid:\t1000\t1001\t1002\t1003
+ Gid:\t1004\t1005\t1006\t1007
+ Threads:\t66
+ Cpus_allowed:\tf
+ Cpus_allowed_list:\t0-7
+ voluntary_ctxt_switches:\t12
+ nonvoluntary_ctxt_switches:\t13""").encode()):
p = psutil.Process()
self.assertEqual(p.num_ctx_switches().voluntary, 12)
self.assertEqual(p.num_ctx_switches().involuntary, 13)
diff --git a/scripts/internal/purge.py b/scripts/internal/purge.py
new file mode 100755
index 00000000..d9301719
--- /dev/null
+++ b/scripts/internal/purge.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Purge psutil installation by removing psutil-related files and
+directories found in site-packages directories. This is needed mainly
+because sometimes "import psutil" imports a leftover installation
+from site-packages directory instead of the main working directory.
+"""
+
+import os
+import shutil
+import site
+
+
+PKGNAME = "psutil"
+
+
+def rmpath(path):
+ if os.path.isdir(path):
+ print("rmdir " + path)
+ shutil.rmtree(path)
+ else:
+ print("rm " + path)
+ os.remove(path)
+
+
+def main():
+ locations = [site.getusersitepackages()]
+ locations.extend(site.getsitepackages())
+ for root in locations:
+ if os.path.isdir(root):
+ for name in os.listdir(root):
+ if PKGNAME in name:
+ abspath = os.path.join(root, name)
+ rmpath(abspath)
+
+
+main()