From 061652b60864f923d474ce5f894d7a3a249a807b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 03:45:11 +0100 Subject: [PATCH 01/15] add disk_swaps() on Linux --- psutil/__init__.py | 21 +++++++++++++++++++++ psutil/_pslinux.py | 33 +++++++++++++++++++++++++++++++++ psutil/tests/__init__.py | 1 + psutil/tests/test_contracts.py | 3 +++ psutil/tests/test_system.py | 16 ++++++++++++++++ 5 files changed, 74 insertions(+) diff --git a/psutil/__init__.py b/psutil/__init__.py index e74bb109ce..623e055576 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -220,6 +220,7 @@ "net_io_counters", "net_connections", "net_if_addrs", # network "net_if_stats", "disk_io_counters", "disk_partitions", "disk_usage", # disk + # "disk_swaps" # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors "users", "boot_time", # others ] @@ -2081,6 +2082,26 @@ def disk_io_counters(perdisk=False, nowrap=True): disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" +# Linux +if hasattr(_psplatform, "disk_swaps"): + + def disk_swaps(): + """Return a list of swap partitions or swap files as a namedtuple + including: + + - path: the path of the swap partition/file on disk + - total: total swap partition/file size + - used: used swap partition/file size + - type: either "partition" or "swapfile" + - priority (Linux): makes sense when multiple swap files are in + use. The lower the priority, the more likely the swap file is + to be used. + """ + return _psplatform.disk_swaps() + + __all__.append("disk_swaps") + + # ===================================================================== # --- network related functions # ===================================================================== diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 9e32f25e7b..a43e7a1503 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -182,6 +182,9 @@ class IOPriority(enum.IntEnum): 'read_time', 'write_time', 'read_merged_count', 'write_merged_count', 'busy_time']) +# psutil.disk_swaps() +sdiskswaps = namedtuple( + 'sdiskswaps', ['path', 'total', 'used', 'fstype', 'opts']) # psutil.Process().open_files() popenfile = namedtuple( 'popenfile', ['path', 'fd', 'position', 'mode', 'flags']) @@ -1182,6 +1185,36 @@ def disk_partitions(all=False): return retlist +def disk_swaps(): + """Return swap partitions (or swap files).""" + retlist = [] + try: + f = open_text("%s/swaps" % get_procfs_path()) + except FileNotFoundError: + return retlist + else: + with f: + lines = f.readlines() + lines.pop(0) # header + for line in lines: + # Format is confusing: + # Filename Type\tSize Used Priority + # /dev/nvme0n1p3 partition\t11718652 2724 -2 + line = line.strip() + name_and_type, _, other_fields = line.partition('\t') + fstype = name_and_type.split()[-1] + # "/dev/nvme0n1p3 partition" -> "/dev/nvme0n1p3" + path = name_and_type.rstrip(fstype).strip() + total, used, priority = map(int, other_fields.split('\t')) + # The priority column is useful when multiple swap + # files are in use. The lower the priority, the + # more likely the swap file is to be used. + opts = "priority=%i" % priority + nt = sdiskswaps(path, total, used, fstype, opts) + retlist.append(nt) + return retlist + + # ===================================================================== # --- sensors # ===================================================================== diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 3e4dc88066..78258f412d 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -171,6 +171,7 @@ HAS_PROC_IO_COUNTERS = hasattr(psutil.Process, "io_counters") HAS_RLIMIT = hasattr(psutil.Process, "rlimit") HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery") +HAS_DISK_SWAPS = hasattr(psutil, "disk_swaps") try: HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery()) except Exception: diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 5e258b3015..fbfd49f7c3 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -133,6 +133,9 @@ def test_battery(self): self.assertEqual(hasattr(psutil, "sensors_battery"), LINUX or WINDOWS or FREEBSD or MACOS) + def test_disk_swaps(self): + self.assertEqual(hasattr(psutil, "disk_swaps"), LINUX) + class TestAvailProcessAPIs(unittest.TestCase): diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 0d3f43753e..296e65d53e 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -39,6 +39,7 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_BATTERY from psutil.tests import HAS_CPU_FREQ +from psutil.tests import HAS_DISK_SWAPS from psutil.tests import HAS_GETLOADAVG from psutil.tests import HAS_NET_IO_COUNTERS from psutil.tests import HAS_SENSORS_BATTERY @@ -716,6 +717,21 @@ def test_disk_io_counters_no_disks(self): self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) assert m.called + @unittest.skipIf(not HAS_DISK_SWAPS, "not supported") + def test_disk_swaps(self): + ls = psutil.disk_swaps() + self.assertIsInstance(ls, list) + if not ls: + raise self.skipTest("no swap disks") + for swap in ls: + assert os.path.exists(swap.path) + self.assertGreaterEqual(swap.total, 0) + self.assertGreaterEqual(swap.used, 0) + self.assertIn(swap.fstype, ("partition", "swapfile")) + self.assertIsInstance(swap.opts, str) + if LINUX: + assert swap.opts.startswith('priority=') + class TestNetAPIs(unittest.TestCase): From 69ff4ff68818f2ced1690722783dc43d97db2da2 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 18:59:31 -0800 Subject: [PATCH 02/15] expose the skeleton of the C function --- psutil/_psutil_windows.c | 2 ++ psutil/_pswindows.py | 4 ++++ psutil/arch/windows/disk.c | 10 ++++++++++ psutil/arch/windows/disk.h | 1 + psutil/arch/windows/ntextapi.h | 2 ++ 5 files changed, 19 insertions(+) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 109ed6c589..b9b7811e45 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -1608,6 +1608,8 @@ PsutilMethods[] = { "Return a list of currently connected users."}, {"disk_partitions", psutil_disk_partitions, METH_VARARGS, "Return disk partitions."}, + {"disk_swaps", psutil_disk_swaps, METH_VARARGS, + "Return information about the disk page files as a list."}, {"net_connections", psutil_net_connections, METH_VARARGS, "Return system-wide connections"}, {"net_if_addrs", psutil_net_if_addrs, METH_VARARGS, diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 99d5d71499..3bbd4fb8fc 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -274,6 +274,10 @@ def disk_partitions(all): return [_common.sdiskpart(*x) for x in rawlist] +def disk_swaps(): + return cext.disk_swaps() + + # ===================================================================== # --- CPU # ===================================================================== diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 45e0ee1e62..8ee0feb1cc 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -344,6 +344,16 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { } +/* + * Return information about the disk page files as a list. + * A page file is basically the same thing as a swap partition. + */ +PyObject * +psutil_disk_swaps(PyObject *self, PyObject *args) { + return Py_BuildValue("i", 77); +} + + /* Accept a filename's drive in native format like "\Device\HarddiskVolume1\" and return the corresponding drive letter (e.g. "C:\\"). diff --git a/psutil/arch/windows/disk.h b/psutil/arch/windows/disk.h index 298fb6ba0e..aa22ad9539 100644 --- a/psutil/arch/windows/disk.h +++ b/psutil/arch/windows/disk.h @@ -9,4 +9,5 @@ PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args); PyObject *psutil_disk_partitions(PyObject *self, PyObject *args); PyObject *psutil_disk_usage(PyObject *self, PyObject *args); +PyObject *psutil_disk_swaps(PyObject *self, PyObject *args); PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args); diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index 8cb00430e2..6163e9ee26 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -35,6 +35,8 @@ typedef LONG NTSTATUS; #define ProcessWow64Information 26 #undef SystemProcessIdInformation #define SystemProcessIdInformation 88 +#undef SystemPagefileInformation +#define SystemPagefileInformation 18 // process suspend() / resume() typedef enum _KTHREAD_STATE { From 6e47170218ade724dac325117d9e6e6f477ccd56 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 19:21:59 -0800 Subject: [PATCH 03/15] expose struct to get pagefile info + invoke NtQuerySystemInformation --- psutil/arch/windows/disk.c | 23 +++++++++++++++++++++++ psutil/arch/windows/ntextapi.h | 13 +++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 8ee0feb1cc..020d18318c 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -350,6 +350,29 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { */ PyObject * psutil_disk_swaps(PyObject *self, PyObject *args) { + NTSTATUS status; + PVOID buffer; + ULONG bufferSize = 0x200; + PSYSTEM_PAGEFILE_INFORMATION pagefile = NULL; + + buffer = MALLOC_ZERO(bufferSize); + while ((status = NtQuerySystemInformation( + SystemPageFileInformation, + buffer, + bufferSize, + NULL)) == STATUS_INFO_LENGTH_MISMATCH) + { + FREE(buffer); + bufferSize *= 2; + buffer = MALLOC_ZERO(bufferSize); + } + + if (! NT_SUCCESS(status)) { + psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation"); + return NULL; + } + + FREE(buffer); return Py_BuildValue("i", 77); } diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index 6163e9ee26..72b934498f 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -18,6 +18,7 @@ typedef LONG NTSTATUS; #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL) // ================================================================ // Enums @@ -35,8 +36,8 @@ typedef LONG NTSTATUS; #define ProcessWow64Information 26 #undef SystemProcessIdInformation #define SystemProcessIdInformation 88 -#undef SystemPagefileInformation -#define SystemPagefileInformation 18 +#undef SystemPageFileInformation +#define SystemPageFileInformation 18 // process suspend() / resume() typedef enum _KTHREAD_STATE { @@ -372,6 +373,14 @@ typedef struct _SYSTEM_PROCESS_ID_INFORMATION { UNICODE_STRING ImageName; } SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION; +typedef struct _SYSTEM_PAGEFILE_INFORMATION { + ULONG NextEntryOffset; + ULONG TotalSize; + ULONG TotalInUse; + ULONG PeakUsage; + UNICODE_STRING PageFileName; +} SYSTEM_PAGEFILE_INFORMATION, *PSYSTEM_PAGEFILE_INFORMATION; + // ==================================================================== // PEB structs for cmdline(), cwd(), environ() // ==================================================================== From cd3a4ed8d7ef99984002f86cb68320410fcf09bf Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 20:01:43 -0800 Subject: [PATCH 04/15] populate the tuple --- psutil/_pslinux.py | 8 ++++---- psutil/_pswindows.py | 11 ++++++++++- psutil/arch/windows/disk.c | 40 +++++++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index a43e7a1503..92ff8ed4d1 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -184,7 +184,7 @@ class IOPriority(enum.IntEnum): 'busy_time']) # psutil.disk_swaps() sdiskswaps = namedtuple( - 'sdiskswaps', ['path', 'total', 'used', 'fstype', 'opts']) + 'sdiskswaps', ['path', 'total', 'used', 'fstype', 'priority']) # psutil.Process().open_files() popenfile = namedtuple( 'popenfile', ['path', 'fd', 'position', 'mode', 'flags']) @@ -1205,12 +1205,12 @@ def disk_swaps(): fstype = name_and_type.split()[-1] # "/dev/nvme0n1p3 partition" -> "/dev/nvme0n1p3" path = name_and_type.rstrip(fstype).strip() - total, used, priority = map(int, other_fields.split('\t')) # The priority column is useful when multiple swap # files are in use. The lower the priority, the # more likely the swap file is to be used. - opts = "priority=%i" % priority - nt = sdiskswaps(path, total, used, fstype, opts) + total, used, priority = map(int, other_fields.split('\t')) + nt = sdiskswaps(path, total, used, # common + fstype, priority) # linux only retlist.append(nt) return retlist diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 3bbd4fb8fc..a0960e011d 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -165,6 +165,10 @@ class IOPriority(enum.IntEnum): ['user', 'system', 'idle', 'interrupt', 'dpc']) # psutil.virtual_memory() svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) +# psutil.disk_swaps() +sdiskswaps = namedtuple( + 'sdiskswaps', ['path', 'total', 'used', # common to all platforms + 'peak']) # Windows specific # psutil.Process.memory_info() pmem = namedtuple( 'pmem', ['rss', 'vms', @@ -275,7 +279,12 @@ def disk_partitions(all): def disk_swaps(): - return cext.disk_swaps() + """Return disk page files information.""" + ret = [] + for total, used, peak in cext.disk_swaps(): + nt = sdiskswaps("path", total, used, peak) + ret.append(nt) + return ret # ===================================================================== diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 020d18318c..f418b0fa15 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -353,8 +353,14 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { NTSTATUS status; PVOID buffer; ULONG bufferSize = 0x200; - PSYSTEM_PAGEFILE_INFORMATION pagefile = NULL; + PSYSTEM_PAGEFILE_INFORMATION pInfo; + PyObject *py_tuple = NULL; + PyObject *py_retlist = PyList_New(0); + if (! py_retlist) + return NULL; + + // Enumerate page files. buffer = MALLOC_ZERO(bufferSize); while ((status = NtQuerySystemInformation( SystemPageFileInformation, @@ -369,11 +375,39 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { if (! NT_SUCCESS(status)) { psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation"); - return NULL; + goto error; + } + + // Traverse the resulting struct. + pInfo = (SYSTEM_PAGEFILE_INFORMATION *)buffer; + while (TRUE) { + // entry + py_tuple = Py_BuildValue( + "kkk", + pInfo->TotalSize * PSUTIL_SYSTEM_INFO.dwPageSize, + pInfo->TotalInUse * PSUTIL_SYSTEM_INFO.dwPageSize, + pInfo->PeakUsage * PSUTIL_SYSTEM_INFO.dwPageSize + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + // end of list + if (pInfo->NextEntryOffset == 0) + break; + // point to next struct + pInfo = (SYSTEM_PAGEFILE_INFORMATION *) \ + ((BYTE *)pInfo + pInfo->NextEntryOffset); } FREE(buffer); - return Py_BuildValue("i", 77); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; } From 015541da25878999c4a3e5c2b57fd22814b04714 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 20:23:59 -0800 Subject: [PATCH 05/15] return the path string --- psutil/_pswindows.py | 9 +++++++-- psutil/arch/windows/disk.c | 11 ++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index a0960e011d..76284958ca 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -200,6 +200,9 @@ def convert_dos_path(s): into: "C:\Windows\systemew\file.txt" """ + # "\??\" refers to \GLOBAL??\. Just remove it. + if s.startswith("\\??\\"): + return s[4:] rawdrive = '\\'.join(s.split('\\')[:3]) driveletter = cext.win32_QueryDosDevice(rawdrive) remainder = s[len(rawdrive):] @@ -281,8 +284,10 @@ def disk_partitions(all): def disk_swaps(): """Return disk page files information.""" ret = [] - for total, used, peak in cext.disk_swaps(): - nt = sdiskswaps("path", total, used, peak) + rawlist = cext.disk_swaps() + for dospath, total, used, peak in rawlist: + path = convert_dos_path(dospath) + nt = sdiskswaps(path, total, used, peak) ret.append(nt) return ret diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index f418b0fa15..621f007c92 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -355,6 +355,7 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { ULONG bufferSize = 0x200; PSYSTEM_PAGEFILE_INFORMATION pInfo; PyObject *py_tuple = NULL; + PyObject *py_path = NULL; PyObject *py_retlist = PyList_New(0); if (! py_retlist) @@ -382,8 +383,14 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { pInfo = (SYSTEM_PAGEFILE_INFORMATION *)buffer; while (TRUE) { // entry + py_path = PyUnicode_FromWideChar( + pInfo->PageFileName.Buffer, + wcslen(pInfo->PageFileName.Buffer)); + if (! py_path) + goto error; py_tuple = Py_BuildValue( - "kkk", + "Okkk", + py_path, pInfo->TotalSize * PSUTIL_SYSTEM_INFO.dwPageSize, pInfo->TotalInUse * PSUTIL_SYSTEM_INFO.dwPageSize, pInfo->PeakUsage * PSUTIL_SYSTEM_INFO.dwPageSize @@ -393,6 +400,7 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { if (PyList_Append(py_retlist, py_tuple)) goto error; Py_CLEAR(py_tuple); + Py_CLEAR(py_path); // end of list if (pInfo->NextEntryOffset == 0) break; @@ -406,6 +414,7 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { error: Py_XDECREF(py_tuple); + Py_XDECREF(py_path); Py_DECREF(py_retlist); return NULL; } From 4415a05987e2ace63b68686dd36e9426659c314b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 20:45:44 -0800 Subject: [PATCH 06/15] fix tests --- psutil/tests/test_contracts.py | 2 +- psutil/tests/test_memory_leaks.py | 5 +++++ psutil/tests/test_system.py | 11 ++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index fbfd49f7c3..e1fa4076fa 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -134,7 +134,7 @@ def test_battery(self): LINUX or WINDOWS or FREEBSD or MACOS) def test_disk_swaps(self): - self.assertEqual(hasattr(psutil, "disk_swaps"), LINUX) + self.assertEqual(hasattr(psutil, "disk_swaps"), LINUX or WINDOWS) class TestAvailProcessAPIs(unittest.TestCase): diff --git a/psutil/tests/test_memory_leaks.py b/psutil/tests/test_memory_leaks.py index 31a632a478..4da9fea5c7 100755 --- a/psutil/tests/test_memory_leaks.py +++ b/psutil/tests/test_memory_leaks.py @@ -40,6 +40,7 @@ from psutil.tests import get_test_subprocess from psutil.tests import HAS_CPU_AFFINITY from psutil.tests import HAS_CPU_FREQ +from psutil.tests import HAS_DISK_SWAPS from psutil.tests import HAS_ENVIRON from psutil.tests import HAS_IONICE from psutil.tests import HAS_MEMORY_MAPS @@ -515,6 +516,10 @@ def test_disk_partitions(self): def test_disk_io_counters(self): self.execute(psutil.disk_io_counters, nowrap=False) + @unittest.skipIf(not HAS_DISK_SWAPS, "not supported") + def test_disk_swaps(self): + self.execute(psutil.disk_swaps) + # --- proc @skip_if_linux() diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 296e65d53e..ddecf52dc8 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -727,10 +727,15 @@ def test_disk_swaps(self): assert os.path.exists(swap.path) self.assertGreaterEqual(swap.total, 0) self.assertGreaterEqual(swap.used, 0) - self.assertIn(swap.fstype, ("partition", "swapfile")) - self.assertIsInstance(swap.opts, str) if LINUX: - assert swap.opts.startswith('priority=') + fields = ('path', 'total', 'used', 'fstype', 'priority') + self.assertEqual(swap._fields, fields) + self.assertIn(swap.fstype, ("partition", "swapfile")) + self.assertIsInstance(swap.priority, int) + elif WINDOWS: + fields = ('path', 'total', 'used', 'peak') + self.assertEqual(swap._fields, fields) + self.assertGreaterEqual(swap.peak, 0) class TestNetAPIs(unittest.TestCase): From 66f5f3306f83ee2b170e9d090a483e5f36c2f162 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 20:55:43 -0800 Subject: [PATCH 07/15] ignore me --- psutil/tests/test_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index ddecf52dc8..3bfc84bc71 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -722,9 +722,9 @@ def test_disk_swaps(self): ls = psutil.disk_swaps() self.assertIsInstance(ls, list) if not ls: - raise self.skipTest("no swap disks") + raise self.skipTest("no swap locations") for swap in ls: - assert os.path.exists(swap.path) + assert os.path.exists(swap.path), swap.path self.assertGreaterEqual(swap.total, 0) self.assertGreaterEqual(swap.used, 0) if LINUX: From 6daa35c479484737cc2b156d7fa6b84d53b87056 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 21:12:00 -0800 Subject: [PATCH 08/15] handle the case where there are no swap/pagefiles --- psutil/arch/windows/disk.c | 57 ++++++++++++++++++---------------- psutil/arch/windows/ntextapi.h | 1 - 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 621f007c92..7f70d6007f 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -380,33 +380,38 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { } // Traverse the resulting struct. + // A TotalSize of 0 is used to indicate that there are no pagefiles. pInfo = (SYSTEM_PAGEFILE_INFORMATION *)buffer; - while (TRUE) { - // entry - py_path = PyUnicode_FromWideChar( - pInfo->PageFileName.Buffer, - wcslen(pInfo->PageFileName.Buffer)); - if (! py_path) - goto error; - py_tuple = Py_BuildValue( - "Okkk", - py_path, - pInfo->TotalSize * PSUTIL_SYSTEM_INFO.dwPageSize, - pInfo->TotalInUse * PSUTIL_SYSTEM_INFO.dwPageSize, - pInfo->PeakUsage * PSUTIL_SYSTEM_INFO.dwPageSize - ); - if (!py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_CLEAR(py_tuple); - Py_CLEAR(py_path); - // end of list - if (pInfo->NextEntryOffset == 0) - break; - // point to next struct - pInfo = (SYSTEM_PAGEFILE_INFORMATION *) \ - ((BYTE *)pInfo + pInfo->NextEntryOffset); + if (pInfo->TotalSize != 0) { + while (TRUE) { + // construct python list + py_path = PyUnicode_FromWideChar( + pInfo->PageFileName.Buffer, + wcslen(pInfo->PageFileName.Buffer)); + if (! py_path) + goto error; + + py_tuple = Py_BuildValue( + "Okkk", + py_path, + pInfo->TotalSize * PSUTIL_SYSTEM_INFO.dwPageSize, + pInfo->TotalInUse * PSUTIL_SYSTEM_INFO.dwPageSize, + pInfo->PeakUsage * PSUTIL_SYSTEM_INFO.dwPageSize + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + Py_CLEAR(py_path); + + // end of list + if (pInfo->NextEntryOffset == 0) + break; + // point to next struct + pInfo = (SYSTEM_PAGEFILE_INFORMATION *) \ + ((BYTE *)pInfo + pInfo->NextEntryOffset); + } } FREE(buffer); diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index 72b934498f..d585c6786e 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -18,7 +18,6 @@ typedef LONG NTSTATUS; #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) -#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL) // ================================================================ // Enums From 68556aeb2b1bd35020d940be61daba86387f3c82 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 13 Feb 2020 21:21:06 -0800 Subject: [PATCH 09/15] FREE(buffer) --- psutil/arch/windows/disk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/psutil/arch/windows/disk.c b/psutil/arch/windows/disk.c index 7f70d6007f..0cf9533188 100644 --- a/psutil/arch/windows/disk.c +++ b/psutil/arch/windows/disk.c @@ -351,7 +351,7 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { PyObject * psutil_disk_swaps(PyObject *self, PyObject *args) { NTSTATUS status; - PVOID buffer; + PVOID buffer = NULL; ULONG bufferSize = 0x200; PSYSTEM_PAGEFILE_INFORMATION pInfo; PyObject *py_tuple = NULL; @@ -408,7 +408,7 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { // end of list if (pInfo->NextEntryOffset == 0) break; - // point to next struct + // set pointer to the next pInfo struct pInfo = (SYSTEM_PAGEFILE_INFORMATION *) \ ((BYTE *)pInfo + pInfo->NextEntryOffset); } @@ -418,6 +418,8 @@ psutil_disk_swaps(PyObject *self, PyObject *args) { return py_retlist; error: + if (buffer != NULL) + FREE(buffer); Py_XDECREF(py_tuple); Py_XDECREF(py_path); Py_DECREF(py_retlist); From b07e957423a53167190973f9aa5f967192ef3777 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 14:13:18 +0100 Subject: [PATCH 10/15] add test + update disk_usage script --- psutil/_pslinux.py | 2 ++ psutil/tests/test_system.py | 5 +++++ scripts/disk_usage.py | 11 +++++++++++ 3 files changed, 18 insertions(+) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 92ff8ed4d1..53e9caddf1 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1209,6 +1209,8 @@ def disk_swaps(): # files are in use. The lower the priority, the # more likely the swap file is to be used. total, used, priority = map(int, other_fields.split('\t')) + total *= 1024 + used *= 1024 nt = sdiskswaps(path, total, used, # common fstype, priority) # linux only retlist.append(nt) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 3bfc84bc71..b803fd725d 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -737,6 +737,11 @@ def test_disk_swaps(self): self.assertEqual(swap._fields, fields) self.assertGreaterEqual(swap.peak, 0) + if LINUX: + self.assertEqual( + psutil.swap_memory().total, + sum([x.total for x in psutil.disk_swaps()])) + class TestNetAPIs(unittest.TestCase): diff --git a/scripts/disk_usage.py b/scripts/disk_usage.py index 1860401fda..2edfdbe01a 100755 --- a/scripts/disk_usage.py +++ b/scripts/disk_usage.py @@ -19,6 +19,7 @@ import os import psutil from psutil._common import bytes2human +from psutil._common import usage_percent def main(): @@ -41,6 +42,16 @@ def main(): int(usage.percent), part.fstype, part.mountpoint)) + if hasattr(psutil, "disk_swaps"): + for swap in psutil.disk_swaps(): + print(templ % ( + swap.path, + bytes2human(swap.total), + bytes2human(swap.used), + bytes2human(swap.total - swap.used), + int(usage_percent(swap.used, swap.total)), + "swap", + "")) if __name__ == '__main__': From 1affff860a0b996e27160c4ab6bac19f70e7e7e1 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 14:31:28 +0100 Subject: [PATCH 11/15] add test using swapon util --- psutil/tests/test_linux.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 97946a0bbd..872ddf50e6 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1204,6 +1204,20 @@ def exists(path): self.assertRaises(NotImplementedError, psutil.disk_io_counters) +@unittest.skipIf(not LINUX, "LINUX only") +class TestDiskSwaps(unittest.TestCase): + + @unittest.skipIf(not which("swapon"), "swapon utility not available") + def test_against_swapon(self): + swaps = psutil.disk_swaps() + lines = sh("swapon --bytes").split('\n') + lines.pop(0) # header + for i, line in enumerate(lines): + path, fstype, total, used, prio = line.split() + self.assertEqual((path, int(total), int(used), fstype, int(prio)), + tuple(swaps[i])) + + # ===================================================================== # --- misc # ===================================================================== From 76af6952b36c837d7aa9da36e7143aebc99723bb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 14:37:32 +0000 Subject: [PATCH 12/15] implement disk_swaps() on FreeBSD --- psutil/_psbsd.py | 6 ++++ psutil/_pslinux.py | 8 +++--- psutil/_psutil_bsd.c | 51 ++++++++++++++++++++++++++++++++++ psutil/_pswindows.py | 8 +++--- psutil/tests/test_contracts.py | 3 +- 5 files changed, 67 insertions(+), 9 deletions(-) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 49ad1e995c..3188a2282a 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -151,6 +151,8 @@ # psutil.cpu_times() scputimes = namedtuple( 'scputimes', ['user', 'nice', 'system', 'idle', 'irq']) +# psutil.disk_swaps() +sdiskswap = namedtuple('sdiskswap', ['path', 'total', 'used']) # psutil.Process.memory_info() pmem = namedtuple('pmem', ['rss', 'vms', 'text', 'data', 'stack']) # psutil.Process.memory_full_info() @@ -336,6 +338,10 @@ def disk_partitions(all=False): disk_usage = _psposix.disk_usage disk_io_counters = cext.disk_io_counters +if hasattr(cext, "disk_swaps"): + def disk_swaps(): + """Return disk page files information.""" + return [sdiskswap(*x) for x in cext.disk_swaps()] # ===================================================================== diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 92ff8ed4d1..176346c82c 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -183,8 +183,8 @@ class IOPriority(enum.IntEnum): 'read_merged_count', 'write_merged_count', 'busy_time']) # psutil.disk_swaps() -sdiskswaps = namedtuple( - 'sdiskswaps', ['path', 'total', 'used', 'fstype', 'priority']) +sdiskswap = namedtuple( + 'sdiskswap', ['path', 'total', 'used', 'fstype', 'priority']) # psutil.Process().open_files() popenfile = namedtuple( 'popenfile', ['path', 'fd', 'position', 'mode', 'flags']) @@ -1209,8 +1209,8 @@ def disk_swaps(): # files are in use. The lower the priority, the # more likely the swap file is to be used. total, used, priority = map(int, other_fields.split('\t')) - nt = sdiskswaps(path, total, used, # common - fstype, priority) # linux only + nt = sdiskswap(path, total, used, # common + fstype, priority) # linux only retlist.append(nt) return retlist diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index 953fcd083c..798550339c 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -691,6 +691,55 @@ psutil_disk_partitions(PyObject *self, PyObject *args) { } +#define NSWAP 16 + +/* + * Enumerate swap locations. + */ +static PyObject * +psutil_disk_swaps(PyObject *self, PyObject *args) { + kvm_t *kd; + int n; + int i; + struct kvm_swap kswap[NSWAP]; + int pagesize = getpagesize(); + PyObject *py_tuple; + PyObject *py_retlist; + + n = kvm_getswapinfo( + kd, kswap, sizeof kswap / sizeof kswap[0], SWIF_DEV_PREFIX); + if (n == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + // For some reason if we don't create the list here kvm_getswapinfo() + // returns 0. + py_retlist = PyList_New(0); + if (! py_retlist) + goto error; + + for (i = 0; i < n; ++i) { + py_tuple = Py_BuildValue( + "sII", + kswap[i].ksw_devname, + kswap[i].ksw_total * pagesize, + kswap[i].ksw_used * pagesize + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + } + + return py_retlist; + +error: + return NULL; +} + + /* * Return a Python list of named tuples with overall network I/O information */ @@ -976,6 +1025,8 @@ static PyMethodDef mod_methods[] = { {"disk_partitions", psutil_disk_partitions, METH_VARARGS, "Return a list of tuples including device, mount point and " "fs type for all partitions mounted on the system."}, + {"disk_swaps", psutil_disk_swaps, METH_VARARGS, + "Enumerate swap partitions/files."}, {"net_io_counters", psutil_net_io_counters, METH_VARARGS, "Return dict of tuples of networks I/O information."}, {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 76284958ca..faa248e29d 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -166,9 +166,9 @@ class IOPriority(enum.IntEnum): # psutil.virtual_memory() svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) # psutil.disk_swaps() -sdiskswaps = namedtuple( - 'sdiskswaps', ['path', 'total', 'used', # common to all platforms - 'peak']) # Windows specific +sdiskswap = namedtuple( + 'sdiskswap', ['path', 'total', 'used', # common to all platforms + 'peak']) # Windows specific # psutil.Process.memory_info() pmem = namedtuple( 'pmem', ['rss', 'vms', @@ -287,7 +287,7 @@ def disk_swaps(): rawlist = cext.disk_swaps() for dospath, total, used, peak in rawlist: path = convert_dos_path(dospath) - nt = sdiskswaps(path, total, used, peak) + nt = sdiskswap(path, total, used, peak) ret.append(nt) return ret diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index e1fa4076fa..85f9c00d2c 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -134,7 +134,8 @@ def test_battery(self): LINUX or WINDOWS or FREEBSD or MACOS) def test_disk_swaps(self): - self.assertEqual(hasattr(psutil, "disk_swaps"), LINUX or WINDOWS) + hasit = LINUX or WINDOWS or FREEBSD + self.assertEqual(hasattr(psutil, "disk_swaps"), hasit) class TestAvailProcessAPIs(unittest.TestCase): From eb0b911f367c01a90dfcc025d82d051eaa8d781d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 14:45:40 +0000 Subject: [PATCH 13/15] add test --- psutil/tests/test_system.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index b803fd725d..b0cc64df41 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -723,6 +723,7 @@ def test_disk_swaps(self): self.assertIsInstance(ls, list) if not ls: raise self.skipTest("no swap locations") + for swap in ls: assert os.path.exists(swap.path), swap.path self.assertGreaterEqual(swap.total, 0) @@ -737,10 +738,13 @@ def test_disk_swaps(self): self.assertEqual(swap._fields, fields) self.assertGreaterEqual(swap.peak, 0) - if LINUX: + if not WINDOWS: self.assertEqual( psutil.swap_memory().total, sum([x.total for x in psutil.disk_swaps()])) + self.assertEqual( + psutil.swap_memory().used, + sum([x.used for x in psutil.disk_swaps()])) class TestNetAPIs(unittest.TestCase): From 6031fa4714dc877250f2a7e411b553335bbf8f98 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 14:57:28 +0000 Subject: [PATCH 14/15] add freebsd specific tests --- psutil/tests/test_bsd.py | 41 ++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index e525e66724..9e7ae4fecf 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -240,17 +240,17 @@ def test_cpu_times(self): @unittest.skipIf(not FREEBSD, "FREEBSD only") class FreeBSDSystemTestCase(unittest.TestCase): - @staticmethod - def parse_swapinfo(): + @unittest.skipIf(not which('swapinfo'), "swapinfo util not available") + def parse_swapinfo(self): # the last line is always the total - output = sh("swapinfo -k").splitlines()[-1] - parts = re.split(r'\s+', output) - - if not parts: - raise ValueError("Can't parse swapinfo: %s" % output) - - # the size is in 1k units, so multiply by 1024 - total, used, free = (int(p) * 1024 for p in parts[1:4]) + total = used = free = 0 + lines = sh("swapinfo -k").splitlines() + lines.pop(0) # header + for line in lines: + path, total_, used_, free_ = line.split()[:4] + total += int(total_) * 1024 + used += int(used_) * 1024 + free += int(free_) * 1024 return total, used, free def test_cpu_frequency_against_sysctl(self): @@ -473,6 +473,27 @@ def test_sensors_temperatures_against_sysctl(self): psutil.sensors_temperatures()["coretemp"][cpu].high, sysctl_result) + # --- disks + + @unittest.skipIf(not which('swapinfo'), "swapinfo util not available") + def test_disk_swaps(self): + total, used, free = self.parse_swapinfo() + self.assertAlmostEqual( + sum([x.total for x in psutil.disk_swaps()]), + total, delta=MEMORY_TOLERANCE) + self.assertAlmostEqual( + sum([x.used for x in psutil.disk_swaps()]), + used, delta=MEMORY_TOLERANCE) + + lines = sh("swapinfo -k").splitlines() + lines.pop(0) # header + paths = [] + for i, line in enumerate(lines): + paths.append(line.split()[0]) + self.assertEqual(sorted(paths), sorted + ([x.path for x in psutil.disk_swaps()])) + + # ===================================================================== # --- OpenBSD # ===================================================================== From 5a2551d9c0dd33e93bb00af279261d6ca78256ff Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Feb 2020 16:22:49 +0100 Subject: [PATCH 15/15] update doc --- Makefile | 2 +- docs/index.rst | 17 +++++++++++++++++ psutil/__init__.py | 5 ++--- psutil/_common.py | 2 ++ psutil/_psbsd.py | 4 +--- psutil/_pslinux.py | 2 +- psutil/_pswindows.py | 3 +-- psutil/tests/test_bsd.py | 4 ++-- 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 890c6e413f..ff71d9c7fc 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ DEPS = \ pyperf \ requests \ setuptools \ - sphinx \ + sphinx==2.3.1 \ twine \ unittest2 \ virtualenv \ diff --git a/docs/index.rst b/docs/index.rst index ea5f15bd12..82b4498ffc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -493,6 +493,23 @@ Disks .. versionchanged:: 4.0.0 NetBSD no longer has *read_time* and *write_time* fields. +.. function:: disk_swaps() + + Enumerate swap partitions and swap files as a list of namedtuples including: + + - **path**: the path of the swap partition/file on disk + - **total**: total swap partition/file size + - **used**: used swap partition/file size + - **fstype** (Linux): either "partition" or "swapfile" + - **priority** (Linux): makes sense when multiple swap files are in use. + The lower the priority, the more likely the swap file is o be used. + - **peak** (Windows): the highest peak usage over time. + + Availability: Linux, Windows, FreeBSD + + .. versionadded:: 5.7.1 + + Network ------- diff --git a/psutil/__init__.py b/psutil/__init__.py index 623e055576..81f1cee966 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -2086,13 +2086,12 @@ def disk_io_counters(perdisk=False, nowrap=True): if hasattr(_psplatform, "disk_swaps"): def disk_swaps(): - """Return a list of swap partitions or swap files as a namedtuple - including: + """Enumerate swap partitions and swap files as a list of namedtuples: - path: the path of the swap partition/file on disk - total: total swap partition/file size - used: used swap partition/file size - - type: either "partition" or "swapfile" + - fstype (Linux): either "partition" or "swapfile" - priority (Linux): makes sense when multiple swap files are in use. The lower the priority, the more likely the swap file is to be used. diff --git a/psutil/_common.py b/psutil/_common.py index 9306cd1556..efa8e36c09 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -176,6 +176,8 @@ class BatteryTime(enum.IntEnum): 'read_time', 'write_time']) # psutil.disk_partitions() sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts']) +# psutil.disk_swaps() +sdiskswap = namedtuple('sdiskswap', ['path', 'total', 'used']) # psutil.net_io_counters() snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv', 'packets_sent', 'packets_recv', diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 3188a2282a..f83c401229 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -151,8 +151,6 @@ # psutil.cpu_times() scputimes = namedtuple( 'scputimes', ['user', 'nice', 'system', 'idle', 'irq']) -# psutil.disk_swaps() -sdiskswap = namedtuple('sdiskswap', ['path', 'total', 'used']) # psutil.Process.memory_info() pmem = namedtuple('pmem', ['rss', 'vms', 'text', 'data', 'stack']) # psutil.Process.memory_full_info() @@ -341,7 +339,7 @@ def disk_partitions(all=False): if hasattr(cext, "disk_swaps"): def disk_swaps(): """Return disk page files information.""" - return [sdiskswap(*x) for x in cext.disk_swaps()] + return [_common.sdiskswap(*x) for x in cext.disk_swaps()] # ===================================================================== diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 19fdde036b..f745d61a22 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -184,7 +184,7 @@ class IOPriority(enum.IntEnum): 'busy_time']) # psutil.disk_swaps() sdiskswap = namedtuple( - 'sdiskswap', ['path', 'total', 'used', 'fstype', 'priority']) + 'sdiskswap', _common.sdiskswap._fields + ('fstype', 'priority')) # psutil.Process().open_files() popenfile = namedtuple( 'popenfile', ['path', 'fd', 'position', 'mode', 'flags']) diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index faa248e29d..0446656c97 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -167,8 +167,7 @@ class IOPriority(enum.IntEnum): svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) # psutil.disk_swaps() sdiskswap = namedtuple( - 'sdiskswap', ['path', 'total', 'used', # common to all platforms - 'peak']) # Windows specific + 'sdiskswap', _common.sdiskswap._fields + ('peak', )) # psutil.Process.memory_info() pmem = namedtuple( 'pmem', ['rss', 'vms', diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index 9e7ae4fecf..69fa9eee16 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -490,8 +490,8 @@ def test_disk_swaps(self): paths = [] for i, line in enumerate(lines): paths.append(line.split()[0]) - self.assertEqual(sorted(paths), sorted - ([x.path for x in psutil.disk_swaps()])) + self.assertEqual(sorted(paths), + sorted([x.path for x in psutil.disk_swaps()])) # =====================================================================