From 3a75258bd381f46bce8404b10b355b4a7c955e0b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 31 Dec 2023 16:47:15 +0100 Subject: [PATCH 01/10] linux tests: refact mock_open_content() --- psutil/tests/test_linux.py | 398 ++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 210 deletions(-) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 12814ea87..52d9e62f9 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -205,12 +205,13 @@ def get_free_version_info(): @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 mock_open_content(pairs): + """Mock open() builtin and forces it to return a certain content + for a given path. `pairs` is a {"path": "content", ...} dict. """ def open_mock(name, *args, **kwargs): - if name == for_path: + if name in pairs: + content = pairs[name] if PY3: if isinstance(content, basestring): return io.StringIO(content) @@ -362,18 +363,17 @@ 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. - 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: + content = 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() + with mock_open_content({'/proc/meminfo': content}) as m: with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") ret = psutil.virtual_memory() @@ -415,23 +415,22 @@ def test_avail_old_percent(self): 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. - 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: + content = 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() + with mock_open_content({'/proc/meminfo': content}) as m: with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called @@ -444,19 +443,18 @@ def test_avail_old_missing_fields(self): # Remove Active(file), Inactive(file) and SReclaimable # from /proc/meminfo and make sure the fallback is used # (free + cached), - with mock_open_content( - "/proc/meminfo", - textwrap.dedent("""\ - Active: 9444728 kB - Active(anon): 6145416 kB - Buffers: 287952 kB - Cached: 4818144 kB - Inactive(file): 1578132 kB - Inactive(anon): 574764 kB - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - """).encode()) as m: + content = textwrap.dedent("""\ + Active: 9444728 kB + Active(anon): 6145416 kB + Buffers: 287952 kB + Cached: 4818144 kB + Inactive(file): 1578132 kB + Inactive(anon): 574764 kB + MemFree: 2057400 kB + MemTotal: 16325648 kB + Shmem: 577588 kB + """).encode() + with mock_open_content({"/proc/meminfo": content}) as m: with warnings.catch_warnings(record=True) as ws: ret = psutil.virtual_memory() assert m.called @@ -468,22 +466,21 @@ def test_avail_old_missing_fields(self): def test_avail_old_missing_zoneinfo(self): # Remove /proc/zoneinfo file. Make sure fallback is used # (free + cached). - 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 - MemFree: 2057400 kB - MemTotal: 16325648 kB - Shmem: 577588 kB - SReclaimable: 346648 kB - """).encode()): + content = 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 + MemFree: 2057400 kB + MemTotal: 16325648 kB + Shmem: 577588 kB + SReclaimable: 346648 kB + """).encode() + with mock_open_content({"/proc/meminfo": content}): with mock_open_exception( "/proc/zoneinfo", IOError(errno.ENOENT, 'no such file or directory')): @@ -498,64 +495,57 @@ def test_avail_old_missing_zoneinfo(self): def test_virtual_memory_mocked(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("""\ - 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()) - 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: + content = 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() + with mock_open_content({"/proc/meminfo": content}) as m: mem = psutil.virtual_memory() assert m.called self.assertEqual(mem.total, 100 * 1024) @@ -657,7 +647,7 @@ def test_emulate_meminfo_has_no_metrics(self): # Emulate a case where /proc/meminfo provides no swap metrics # in which case sysinfo() syscall is supposed to be used # as a fallback. - with mock_open_content("/proc/meminfo", b"") as m: + with mock_open_content({"/proc/meminfo": b""}) as m: psutil.swap_memory() assert m.called @@ -747,7 +737,7 @@ def test_emulate_fallbacks(self): # Finally, let's make /proc/cpuinfo return meaningless data; # this way we'll fall back on relying on /proc/stat - with mock_open_content('/proc/cpuinfo', b"") as m: + with mock_open_content({"/proc/cpuinfo": b""}) as m: self.assertEqual(psutil._pslinux.cpu_count_logical(), original) assert m.called @@ -1112,14 +1102,13 @@ def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop): psutil.net_connections(kind='inet6') def test_emulate_unix(self): - 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: + content = 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 + """) + with mock_open_content({"/proc/net/unix": content}) as m: psutil.net_connections(kind='unix') assert m.called @@ -1199,9 +1188,8 @@ class TestSystemDiskIoCounters(PsutilTestCase): def test_emulate_kernel_2_4(self): # Tests /proc/diskstats parsing format for 2.4 kernels, see: # https://github.com/giampaolo/psutil/issues/767 - with mock_open_content( - '/proc/diskstats', - " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12"): + content = " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12" + with mock_open_content({'/proc/diskstats': content}): with mock.patch('psutil._pslinux.is_storage_device', return_value=True): ret = psutil.disk_io_counters(nowrap=False) @@ -1219,9 +1207,8 @@ def test_emulate_kernel_2_6_full(self): # Tests /proc/diskstats parsing format for 2.6 kernels, # lines reporting all metrics: # https://github.com/giampaolo/psutil/issues/767 - with mock_open_content( - '/proc/diskstats', - " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11"): + content = " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11" + with mock_open_content({"/proc/diskstats": content}): with mock.patch('psutil._pslinux.is_storage_device', return_value=True): ret = psutil.disk_io_counters(nowrap=False) @@ -1241,9 +1228,7 @@ def test_emulate_kernel_2_6_limited(self): # amount of metrics when it bumps into a partition # (instead of a disk). See: # https://github.com/giampaolo/psutil/issues/767 - with mock_open_content( - '/proc/diskstats', - " 3 1 hda 1 2 3 4"): + with mock_open_content({"/proc/diskstats": " 3 1 hda 1 2 3 4"}): with mock.patch('psutil._pslinux.is_storage_device', return_value=True): ret = psutil.disk_io_counters(nowrap=False) @@ -1262,12 +1247,11 @@ def test_emulate_include_partitions(self): # Make sure that when perdisk=True disk partitions are returned, # see: # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 - with mock_open_content( - '/proc/diskstats', - textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """)): + content = textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """) + with mock_open_content({"/proc/diskstats": content}): with mock.patch('psutil._pslinux.is_storage_device', return_value=False): ret = psutil.disk_io_counters(perdisk=True, nowrap=False) @@ -1281,12 +1265,11 @@ def test_emulate_exclude_partitions(self): # Make sure that when perdisk=False partitions (e.g. 'sda1', # 'nvme0n1p1') are skipped and not included in the total count. # https://github.com/giampaolo/psutil/pull/1313#issuecomment-408626842 - with mock_open_content( - '/proc/diskstats', - textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """)): + content = textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """) + with mock_open_content({"/proc/diskstats": content}): with mock.patch('psutil._pslinux.is_storage_device', return_value=False): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) @@ -1296,12 +1279,11 @@ def test_emulate_exclude_partitions(self): def is_storage_device(name): return name == 'nvme0n1' - with mock_open_content( - '/proc/diskstats', - textwrap.dedent("""\ - 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 - 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 - """)): + content = textwrap.dedent("""\ + 3 0 nvme0n1 1 2 3 4 5 6 7 8 9 10 11 + 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 + """) + with mock_open_content({"/proc/diskstats": content}): with mock.patch('psutil._pslinux.is_storage_device', create=True, side_effect=is_storage_device): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) @@ -1468,13 +1450,12 @@ def open_mock(name, *args, **kwargs): def test_cpu_steal_decrease(self): # Test cumulative cpu stats decrease. We should ignore this. # See issue #1210. - 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: + content = 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() + with mock_open_content({"/proc/stat": content}) 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 @@ -1484,13 +1465,12 @@ def test_cpu_steal_decrease(self): psutil.cpu_times_percent() psutil.cpu_times_percent(percpu=True) - 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: + content = 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() + with mock_open_content({"/proc/stat": content}): # Increase "user" while steal goes "backwards" to zero. cpu_percent = psutil.cpu_percent() assert m.called @@ -1559,7 +1539,7 @@ def test_pid_exists_no_proc_status(self): # 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(). - with mock_open_content("/proc/%s/status", "") as m: + with mock_open_content({"/proc/%s/status": ""}) as m: assert psutil.pid_exists(os.getpid()) assert m.called @@ -1667,7 +1647,7 @@ def open_mock(name, *args, **kwargs): def test_emulate_energy_full_0(self): # Emulate a case where energy_full files returns 0. with mock_open_content( - "/sys/class/power_supply/BAT0/energy_full", b"0") as m: + {"/sys/class/power_supply/BAT0/energy_full": b"0"}) as m: self.assertEqual(psutil.sensors_battery().percent, 0) assert m.called @@ -1681,7 +1661,7 @@ def test_emulate_energy_full_not_avail(self): "/sys/class/power_supply/BAT0/charge_full", IOError(errno.ENOENT, "")): with mock_open_content( - "/sys/class/power_supply/BAT0/capacity", b"88"): + {"/sys/class/power_supply/BAT0/capacity": b"88"}): self.assertEqual(psutil.sensors_battery().percent, 88) def test_emulate_no_power(self): @@ -1834,31 +1814,30 @@ def test_parse_smaps_vs_memory_maps(self): def test_parse_smaps_mocked(self): # See: https://github.com/giampaolo/psutil/issues/1222 - 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: + content = 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() + with mock_open_content({"/proc/%s/smaps" % os.getpid(): content}) as m: p = psutil._pslinux.Process(os.getpid()) uss, pss, swap = p._parse_smaps() assert m.called @@ -2128,7 +2107,7 @@ def test_stat_file_parsing(self): "7", # delayacct_blkio_ticks ] content = " ".join(args).encode() - with mock_open_content('/proc/%s/stat' % os.getpid(), content): + 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) @@ -2144,16 +2123,15 @@ def test_stat_file_parsing(self): self.assertEqual(p.cpu_num(), 6) def test_status_file_parsing(self): - 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()): + content = 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() + with mock_open_content({"/proc/%s/status" % os.getpid(): content}): p = psutil.Process() self.assertEqual(p.num_ctx_switches().voluntary, 12) self.assertEqual(p.num_ctx_switches().involuntary, 13) From d9230a94074dd76832c3f9bfac129175a3c4c99f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 3 Jan 2024 18:59:46 +0100 Subject: [PATCH 02/10] add SECURITY.md Signed-off-by: Giampaolo Rodola --- MANIFEST.in | 1 + SECURITY.md | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 SECURITY.md diff --git a/MANIFEST.in b/MANIFEST.in index ae8056349..14ede9455 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ include LICENSE include MANIFEST.in include Makefile include README.rst +include SECURITY.md include docs/.readthedocs.yaml include docs/DEVGUIDE.rst include docs/DEVNOTES diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..21dc174c7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report +it privately. **Do not disclose it as a public issue**. This gives me time to +fix the issue before public exposure, reducing the chance that an exploit will +be used before a patch is released. + +To report a security vulnerability use the [Tidelift security +contact](https://tidelift.com/security). Tidelift will coordinate the fix and +the disclosure of the reported problem. From 49efffcc52622af6d342bc1b045d8246dca3d7f6 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jan 2024 02:16:12 +0100 Subject: [PATCH 03/10] add more ruff rules --- MANIFEST.in | 1 + SECURITY.md | 10 ++++ docs/conf.py | 7 ++- psutil/__init__.py | 64 +++++++++++++++----------- psutil/_common.py | 8 ++-- psutil/_compat.py | 2 +- psutil/_pslinux.py | 8 ++-- psutil/_psposix.py | 3 +- psutil/_pswindows.py | 14 +++--- psutil/tests/__init__.py | 6 +-- psutil/tests/runner.py | 2 +- psutil/tests/test_contracts.py | 2 +- psutil/tests/test_linux.py | 2 +- psutil/tests/test_process.py | 4 +- psutil/tests/test_windows.py | 4 +- pyproject.toml | 28 ++++++----- scripts/internal/check_broken_links.py | 2 +- scripts/internal/git_pre_commit.py | 8 ++-- scripts/internal/print_downloads.py | 2 +- scripts/killall.py | 6 +-- scripts/nettop.py | 2 +- scripts/who.py | 2 +- setup.py | 11 +++-- 23 files changed, 114 insertions(+), 84 deletions(-) create mode 100644 SECURITY.md diff --git a/MANIFEST.in b/MANIFEST.in index ae8056349..14ede9455 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ include LICENSE include MANIFEST.in include Makefile include README.rst +include SECURITY.md include docs/.readthedocs.yaml include docs/DEVGUIDE.rst include docs/DEVNOTES diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..21dc174c7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report +it privately. **Do not disclose it as a public issue**. This gives me time to +fix the issue before public exposure, reducing the chance that an exploit will +be used before a patch is released. + +To report a security vulnerability use the [Tidelift security +contact](https://tidelift.com/security). Tidelift will coordinate the fix and +the disclosure of the reported problem. diff --git a/docs/conf.py b/docs/conf.py index 1079293de..3adde46eb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,9 +43,8 @@ def get_version(): for num in ret.split('.'): assert num.isdigit(), ret return ret - else: - msg = "couldn't find version string" - raise ValueError(msg) + msg = "couldn't find version string" + raise ValueError(msg) VERSION = get_version() @@ -333,7 +332,7 @@ def get_version(): # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'psutil', 'psutil Documentation', - [author], 1) + [author], 1), ] # If true, show URL addresses after external links. diff --git a/psutil/__init__.py b/psutil/__init__.py index c4686ae2a..3e12b5de2 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -317,17 +317,16 @@ def _init(self, pid, _ignore_nsp=False): pid = os.getpid() else: if not _PY3 and not isinstance(pid, (int, long)): - raise TypeError('pid must be an integer (got %r)' % pid) + msg = "pid must be an integer (got %r)" % pid + raise TypeError(msg) if pid < 0: - raise ValueError('pid must be a positive integer (got %s)' - % pid) + msg = "pid must be a positive integer (got %s)" % pid + raise ValueError(msg) try: _psplatform.cext.check_pid_range(pid) except OverflowError: - raise NoSuchProcess( - pid, - msg='process PID out of range (got %s)' % pid, - ) + msg = "process PID out of range (got %s)" % pid + raise NoSuchProcess(pid, msg=msg) self._pid = pid self._name = None @@ -359,7 +358,8 @@ def _init(self, pid, _ignore_nsp=False): pass except NoSuchProcess: if not _ignore_nsp: - raise NoSuchProcess(pid, msg='process PID not found') + msg = "process PID not found" + raise NoSuchProcess(pid, msg=msg) else: self._gone = True # This pair is supposed to identify a Process instance @@ -523,13 +523,16 @@ def as_dict(self, attrs=None, ad_value=None): valid_names = _as_dict_attrnames if attrs is not None: if not isinstance(attrs, (list, tuple, set, frozenset)): - raise TypeError("invalid attrs type %s" % type(attrs)) + msg = "invalid attrs type %s" % type(attrs) + raise TypeError(msg) attrs = set(attrs) invalid_names = attrs - valid_names if invalid_names: - raise ValueError("invalid attr name%s %s" % ( + msg = "invalid attr name%s %s" % ( "s" if len(invalid_names) > 1 else "", - ", ".join(map(repr, invalid_names)))) + ", ".join(map(repr, invalid_names)), + ) + raise ValueError(msg) retdict = {} ls = attrs or valid_names @@ -1006,7 +1009,8 @@ def cpu_percent(self, interval=None): """ blocking = interval is not None and interval > 0.0 if interval is not None and interval < 0: - raise ValueError("interval is not positive (got %r)" % interval) + msg = "interval is not positive (got %r)" % interval + raise ValueError(msg) num_cpus = cpu_count() or 1 def timer(): @@ -1115,8 +1119,10 @@ def memory_percent(self, memtype="rss"): """ valid_types = list(_psplatform.pfullmem._fields) if memtype not in valid_types: - raise ValueError("invalid memtype %r; valid types are %r" % ( - memtype, tuple(valid_types))) + msg = "invalid memtype %r; valid types are %r" % ( + memtype, tuple(valid_types), + ) + raise ValueError(msg) fun = self.memory_info if memtype in _psplatform.pmem._fields else \ self.memory_full_info metrics = fun() @@ -1126,10 +1132,11 @@ def memory_percent(self, memtype="rss"): total_phymem = _TOTAL_PHYMEM or virtual_memory().total if not total_phymem > 0: # we should never get here - raise ValueError( - "can't calculate process memory percent because " - "total physical system memory is not positive (%r)" - % total_phymem) + msg = ( + "can't calculate process memory percent because total physical" + " system memory is not positive (%r)" % (total_phymem) + ) + raise ValueError(msg) return (value / float(total_phymem)) * 100 if hasattr(_psplatform.Process, "memory_maps"): @@ -1380,8 +1387,10 @@ def __getattribute__(self, name): try: return object.__getattribute__(self.__subproc, name) except AttributeError: - raise AttributeError("%s instance has no attribute '%s'" - % (self.__class__.__name__, name)) + msg = "%s instance has no attribute '%s'" % ( + self.__class__.__name__, name, + ) + raise AttributeError(msg) def wait(self, timeout=None): if self.__subproc.returncode is not None: @@ -1558,7 +1567,8 @@ def check_gone(proc, timeout): gone = set() alive = set(procs) if callback is not None and not callable(callback): - raise TypeError("callback %r is not a callable" % callable) + msg = "callback %r is not a callable" % callback + raise TypeError(msg) if timeout is not None: deadline = _timer() + timeout @@ -1650,15 +1660,15 @@ def cpu_times(percpu=False): try: _last_cpu_times = {threading.current_thread().ident: cpu_times()} -except Exception: +except Exception: # noqa: BLE001 # Don't want to crash at import time. _last_cpu_times = {} try: _last_per_cpu_times = { - threading.current_thread().ident: cpu_times(percpu=True) + threading.current_thread().ident: cpu_times(percpu=True), } -except Exception: +except Exception: # noqa: BLE001 # Don't want to crash at import time. _last_per_cpu_times = {} @@ -1757,7 +1767,8 @@ def cpu_percent(interval=None, percpu=False): tid = threading.current_thread().ident blocking = interval is not None and interval > 0.0 if interval is not None and interval < 0: - raise ValueError("interval is not positive (got %r)" % interval) + msg = "interval is not positive (got %r)" % interval + raise ValueError(msg) def calculate(t1, t2): times_delta = _cpu_times_deltas(t1, t2) @@ -1816,7 +1827,8 @@ def cpu_times_percent(interval=None, percpu=False): tid = threading.current_thread().ident blocking = interval is not None and interval > 0.0 if interval is not None and interval < 0: - raise ValueError("interval is not positive (got %r)" % interval) + msg = "interval is not positive (got %r)" % interval + raise ValueError(msg) def calculate(t1, t2): nums = [] diff --git a/psutil/_common.py b/psutil/_common.py index 4057f541b..95323aabc 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -434,7 +434,7 @@ def wrapper(*args, **kwargs): except KeyError: try: ret = cache[key] = fun(*args, **kwargs) - except Exception as err: + except Exception as err: # noqa: BLE001 raise raise_from(err, None) return ret @@ -482,14 +482,14 @@ def wrapper(self): # case 2: we never entered oneshot() ctx try: return fun(self) - except Exception as err: + except Exception as err: # noqa: BLE001 raise raise_from(err, None) except KeyError: # case 3: we entered oneshot() ctx but there's no cache # for this entry yet try: ret = fun(self) - except Exception as err: + except Exception as err: # noqa: BLE001 raise raise_from(err, None) try: self._cache[fun] = ret @@ -866,7 +866,7 @@ def term_supports_colors(file=sys.stdout): # pragma: no cover assert file.isatty() curses.setupterm() assert curses.tigetnum("colors") > 0 - except Exception: + except Exception: # noqa: BLE001 return False else: return True diff --git a/psutil/_compat.py b/psutil/_compat.py index 613bd8a2b..4957b22fd 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -427,7 +427,7 @@ def get_terminal_size(fallback=(80, 24)): res = struct.unpack( 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')) return (res[1], res[0]) - except Exception: + except Exception: # noqa: BLE001 return fallback diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 8c253d2f0..a252f3850 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -67,7 +67,7 @@ # connection status constants "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING" + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ] @@ -154,7 +154,7 @@ class IOPriority(enum.IntEnum): "08": _common.CONN_CLOSE_WAIT, "09": _common.CONN_LAST_ACK, "0A": _common.CONN_LISTEN, - "0B": _common.CONN_CLOSING + "0B": _common.CONN_CLOSING, } @@ -283,7 +283,7 @@ def set_scputimes_ntuple(procfs_path): try: set_scputimes_ntuple("/proc") -except Exception as err: # pragma: no cover +except Exception as err: # noqa: BLE001 # Don't want to crash at import time. debug("ignoring exception on import: %r" % err) scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0) @@ -2033,7 +2033,7 @@ def get_blocks(lines, current_block): data.get(b'Private_Dirty:', 0), data.get(b'Referenced:', 0), data.get(b'Anonymous:', 0), - data.get(b'Swap:', 0) + data.get(b'Swap:', 0), )) return ls diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 408cd56c7..22d7c4f0d 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -138,7 +138,8 @@ def sleep(interval): # WNOHANG flag was used and PID is still running. interval = sleep(interval) continue - elif os.WIFEXITED(status): + + if os.WIFEXITED(status): # Process terminated normally by calling exit(3) or _exit(2), # or by returning from main(). The return value is the # positive integer passed to *exit(). diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index c536b24e3..f44a7010c 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -717,14 +717,12 @@ def wrapper(self, *args, **kwargs): time.sleep(delay) delay = min(delay * 2, 0.04) continue - else: - raise - else: - msg = ( - "{} retried {} times, converted to AccessDenied as it's " - "still returning {}".format(fun, times, err) - ) - raise AccessDenied(pid=self.pid, name=self._name, msg=msg) + raise + msg = ( + "{} retried {} times, converted to AccessDenied as it's still" + "returning {}".format(fun, times, err) + ) + raise AccessDenied(pid=self.pid, name=self._name, msg=msg) return wrapper diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index f28e13bc4..0dd92b8b9 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -197,7 +197,7 @@ def macos_version(): os.path.join(os.path.dirname(__file__), '..', '..')) SCRIPTS_DIR = os.environ.get( "PSUTIL_SCRIPTS_DIR", - os.path.join(ROOT_DIR, 'scripts') + os.path.join(ROOT_DIR, 'scripts'), ) HERE = os.path.realpath(os.path.dirname(__file__)) @@ -217,7 +217,7 @@ def macos_version(): HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery") try: HAS_BATTERY = HAS_SENSORS_BATTERY and bool(psutil.sensors_battery()) -except Exception: +except Exception: # noqa: BLE001 HAS_BATTERY = False HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans") HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures") @@ -232,7 +232,7 @@ def attempt(exe): try: subprocess.check_call( [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except Exception: + except subprocess.CalledProcessError: return None else: return exe diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py index d3938b05a..a678b4c41 100755 --- a/psutil/tests/runner.py +++ b/psutil/tests/runner.py @@ -222,7 +222,7 @@ def _split_suite(suite): for test in suite: if test.countTestCases() == 0: continue - elif isinstance(test, unittest.TestSuite): + if isinstance(test, unittest.TestSuite): test_class = test._tests[0].__class__ elif isinstance(test, unittest.TestCase): test_class = test diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 0bf03b53d..9c54299a2 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -440,7 +440,7 @@ def test_all(self): meth = getattr(self, name) try: meth(value, info) - except Exception: + except Exception: # noqa: BLE001 s = '\n' + '=' * 70 + '\n' s += "FAIL: name=test_%s, pid=%s, ret=%s\ninfo=%s\n" % ( name, info['pid'], repr(value), info) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 52d9e62f9..5c62842a4 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1630,7 +1630,7 @@ def test_emulate_power_undetermined(self): def open_mock(name, *args, **kwargs): if name.startswith( ('/sys/class/power_supply/AC0/online', - '/sys/class/power_supply/AC/online') + '/sys/class/power_supply/AC/online'), ): raise IOError(errno.ENOENT, "") elif name.startswith("/sys/class/power_supply/BAT0/status"): diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index f1765d3e9..e9a8f3ae6 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -647,7 +647,7 @@ def test_memory_maps(self): value = getattr(nt, fname) if fname == 'path': continue - elif fname in ('addr', 'perms'): + if fname in ('addr', 'perms'): assert value, value else: self.assertIsInstance(value, (int, long)) @@ -1412,7 +1412,7 @@ def clean_dict(d): @unittest.skipIf(not POSIX, "POSIX only") @unittest.skipIf( MACOS_11PLUS, - "macOS 11+ can't get another process environment, issue #2084" + "macOS 11+ can't get another process environment, issue #2084", ) def test_weird_environ(self): # environment variables can contain values without an equals sign diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index 47d6ad3f1..dfc8444bd 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -608,7 +608,7 @@ def test_memory_vms(self): # bytes but funnily enough on certain platforms bytes are # returned instead. wmi_usage = int(w.PageFileUsage) - if (vms != wmi_usage) and (vms != wmi_usage * 1024): + if vms not in (wmi_usage, wmi_usage * 1024): raise self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) def test_create_time(self): @@ -822,7 +822,7 @@ def test_win_service_iter(self): "pause_pending", "continue_pending", "stop_pending", - "stopped" + "stopped", ]) for serv in psutil.win_service_iter(): data = serv.as_dict() diff --git a/pyproject.toml b/pyproject.toml index 6f2aeb032..6e5e9563a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,20 +12,19 @@ select = [ "D301", # Use `r"""` if any backslashes in a docstring "D403", # [*] First word of the first line should be capitalized "PERF102", # [*] When using only the keys of a dict use the `keys()` method + "RET507", # Unnecessary `elif` after `continue` statement "S113", # Probable use of requests call without timeout "S602", # `subprocess` call with `shell=True` identified, security issue ] ignore = [ - "A", # flake8-builtins + "A", # flake8-builtins (shadowing of builtins like all, any, ...) "ANN", # flake8-annotations - "ARG", # flake8-unused-arguments + "ARG001", # unused-function-argument + "ARG002", # unused-method-argument "B007", # Loop control variable `x` not used within loop body "B904", # Within an `except` clause, raise exceptions with `raise ... from err` (PYTHON2.7 COMPAT) - "BLE001", # Do not catch blind exception: `Exception` "C4", # flake8-comprehensions (PYTHON2.7 COMPAT) - "C408", # Unnecessary `dict` call (rewrite as a literal) "C90", # mccabe (function `X` is too complex) - "COM812", # Trailing comma missing "D", # pydocstyle "DTZ", # flake8-datetimez "ERA001", # Found commented-out code @@ -35,16 +34,21 @@ ignore = [ "INP", # flake8-no-pep420 "N801", # Class name `async_chat` should use CapWords convention (ASYNCORE COMPAT) "N802", # Function name X should be lowercase. - "N803", # Argument name X should be lowercase. "N806", # Variable X in function should be lowercase. "N818", # Exception name `FooBar` should be named with an Error suffix "PERF", # Perflint "PGH004", # Use specific rule codes when using `noqa` - "PLR", # pylint - "PLW", # pylint + "PLR0911", # Too many return statements (8 > 6) + "PLR0912", # Too many branches (x > y) + "PLR0913", # Too many arguments in function definition (x > y) + "PLR0915", # Too many statements (92 > 50) + "PLR2004", # Magic value used in comparison, consider replacing X with a constant variable + "PLR5501", # Use `elif` instead of `else` then `if`, to reduce indentation + "PLW0603", # Using the global statement to update `lineno` is discouraged + "PLW2901", # `for` loop variable `x` overwritten by assignment target "PT", # flake8-pytest-style "PTH", # flake8-use-pathlib - "PYI", # flake8-pyi + "PYI", # flake8-pyi (python types stuff) "Q000", # Single quotes found but double quotes preferred "RET", # flake8-return "RUF", # Ruff-specific rules @@ -55,7 +59,6 @@ ignore = [ "SIM117", # Use a single `with` statement with multiple contexts instead of nested `with` statements "SLF", # flake8-self "TD", # all TODOs, XXXs, etc. - "TRY003", # Avoid specifying long messages outside the exception class "TRY200", # Use `raise from` to specify exception cause (PYTHON2.7 COMPAT) "TRY300", # Consider moving this statement to an `else` block "TRY301", # Abstract `raise` to an inner function @@ -69,8 +72,11 @@ ignore = [ [tool.ruff.per-file-ignores] # T201 == print(), T203 == pprint() +# EM101 == raw-string-in-exception +# TRY003 == raise-vanilla-args ".github/workflows/*" = ["T201", "T203"] -"psutil/tests/*" = ["EM101"] # raw-string-in-exception +"psutil/_compat.py" = ["PLW0127"] # self-assigning-variable +"psutil/tests/*" = ["EM101", "TRY003"] "psutil/tests/runner.py" = ["T201", "T203"] "scripts/*" = ["T201", "T203"] "scripts/internal/*" = ["T201", "T203"] diff --git a/scripts/internal/check_broken_links.py b/scripts/internal/check_broken_links.py index 315f06fa1..af53b878c 100755 --- a/scripts/internal/check_broken_links.py +++ b/scripts/internal/check_broken_links.py @@ -213,7 +213,7 @@ def parallel_validator(urls): fname, url = fut_to_url[fut] try: ok = fut.result() - except Exception: + except Exception: # noqa: BLE001 fails.append((fname, url)) print() print("warn: error while validating %s" % url, file=sys.stderr) diff --git a/scripts/internal/git_pre_commit.py b/scripts/internal/git_pre_commit.py index 92852f836..79582feeb 100755 --- a/scripts/internal/git_pre_commit.py +++ b/scripts/internal/git_pre_commit.py @@ -29,7 +29,7 @@ def term_supports_colors(): assert sys.stderr.isatty() curses.setupterm() assert curses.tigetnum("colors") > 0 - except Exception: + except Exception: # noqa: BLE001 return False return True @@ -60,7 +60,7 @@ def sh(cmd): cmd = shlex.split(cmd) p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True + universal_newlines=True, ) stdout, stderr = p.communicate() if p.returncode != 0: @@ -89,7 +89,7 @@ def git_commit_files(): x for x in out.split("\n") if x.endswith(".toml") and os.path.exists(x) ] new_rm_mv = sh( - ["git", "diff", "--name-only", "--diff-filter=ADR", "--cached"] + ["git", "diff", "--name-only", "--diff-filter=ADR", "--cached"], ) # XXX: we should escape spaces and possibly other amenities here new_rm_mv = new_rm_mv.split() @@ -102,7 +102,7 @@ def ruff(files): if subprocess.call(cmd) != 0: return exit( "Python code didn't pass 'ruff' style check." - "Try running 'make fix-ruff'." + "Try running 'make fix-ruff'.", ) diff --git a/scripts/internal/print_downloads.py b/scripts/internal/print_downloads.py index 1ee37e534..8e47a08b4 100755 --- a/scripts/internal/print_downloads.py +++ b/scripts/internal/print_downloads.py @@ -144,7 +144,7 @@ def main(): data = [ {'what': 'Per month', 'download_count': downs}, {'what': 'Per day', 'download_count': int(downs / 30)}, - {'what': 'PYPI ranking', 'download_count': ranking()} + {'what': 'PYPI ranking', 'download_count': ranking()}, ] print_markdown_table('Overview', 'what', data) print_markdown_table('Operating systems', 'system_name', diff --git a/scripts/killall.py b/scripts/killall.py index 0308370de..592b8d6e3 100755 --- a/scripts/killall.py +++ b/scripts/killall.py @@ -16,15 +16,15 @@ def main(): if len(sys.argv) != 2: sys.exit('usage: %s name' % __file__) else: - NAME = sys.argv[1] + name = sys.argv[1] killed = [] for proc in psutil.process_iter(): - if proc.name() == NAME and proc.pid != os.getpid(): + if proc.name() == name and proc.pid != os.getpid(): proc.kill() killed.append(proc.pid) if not killed: - sys.exit('%s: no process found' % NAME) + sys.exit('%s: no process found' % name) else: sys.exit(0) diff --git a/scripts/nettop.py b/scripts/nettop.py index fedc644d0..64d408fc4 100755 --- a/scripts/nettop.py +++ b/scripts/nettop.py @@ -82,7 +82,7 @@ def refresh_window(tot_before, tot_after, pnic_before, pnic_after): # totals printl("total bytes: sent: %-10s received: %s" % ( bytes2human(tot_after.bytes_sent), - bytes2human(tot_after.bytes_recv)) + bytes2human(tot_after.bytes_recv)), ) printl("total packets: sent: %-10s received: %s" % ( tot_after.packets_sent, tot_after.packets_recv)) diff --git a/scripts/who.py b/scripts/who.py index 18db1b17a..77c474ff9 100755 --- a/scripts/who.py +++ b/scripts/who.py @@ -26,7 +26,7 @@ def main(): user.terminal or '-', datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"), "(%s)" % user.host if user.host else "", - proc_name + proc_name, )) diff --git a/setup.py b/setup.py index ae25116ae..1ab704854 100755 --- a/setup.py +++ b/setup.py @@ -230,7 +230,7 @@ def get_winver(): ], # extra_compile_args=["/W 4"], # extra_link_args=["/DEBUG"], - **py_limited_api + **py_limited_api # noqa (noqa needed for python 2.7) ) elif MACOS: @@ -244,7 +244,7 @@ def get_winver(): ), define_macros=macros, extra_link_args=[ - '-framework', 'CoreFoundation', '-framework', 'IOKit' + '-framework', 'CoreFoundation', '-framework', 'IOKit', ], **py_limited_api) @@ -313,7 +313,7 @@ def get_winver(): sources=sources + [ 'psutil/_psutil_sunos.c', 'psutil/arch/solaris/v10/ifaddrs.c', - 'psutil/arch/solaris/environ.c' + 'psutil/arch/solaris/environ.c', ], define_macros=macros, libraries=['kstat', 'nsl', 'socket'], @@ -473,7 +473,10 @@ def main(): elif which('rpm'): missdeps("sudo yum install gcc %s%s-devel" % (pyimpl, py3)) elif which('apk'): - missdeps("sudo apk add gcc %s%s-dev musl-dev linux-headers" % (pyimpl, py3)) + missdeps( + "sudo apk add gcc %s%s-dev musl-dev linux-headers" % ( + pyimpl, py3), + ) elif MACOS: print(hilite("XCode (https://developer.apple.com/xcode/) " "is not installed", color="red"), file=sys.stderr) From 3882a56488d411103ba556d0ca9ad66f4aa34d0c Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jan 2024 11:35:57 +0100 Subject: [PATCH 04/10] some tests refactoring --- psutil/tests/test_connections.py | 20 +-- psutil/tests/test_process.py | 201 ++++++++++++++++--------------- 2 files changed, 113 insertions(+), 108 deletions(-) diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index 13d1aebe9..70929b84c 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -52,17 +52,19 @@ class ConnectionTestCase(PsutilTestCase): def setUp(self): - if not (NETBSD or FREEBSD): - # process opens a UNIX socket to /var/log/run. - cons = thisproc.connections(kind='all') - assert not cons, cons + if NETBSD or FREEBSD or (MACOS and not PY3): + # Process opens a UNIX socket to /var/log/run. + return + cons = thisproc.connections(kind='all') + assert not cons, cons def tearDown(self): - if not (FREEBSD or NETBSD): - # Make sure we closed all resources. - # NetBSD opens a UNIX socket to /var/log/run. - cons = thisproc.connections(kind='all') - assert not cons, cons + # Make sure we closed all resources. + # Some BSDs open a UNIX socket to /var/log/run. + if NETBSD or FREEBSD or (MACOS and not PY3): + return + cons = thisproc.connections(kind='all') + assert not cons, cons def compare_procsys_connections(self, pid, proc_cons, kind='all'): """Given a process PID and its list of connections compare diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index e9a8f3ae6..d7d06854c 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -347,6 +347,13 @@ def test_io_counters(self): @unittest.skipIf(not HAS_IONICE, "not supported") @unittest.skipIf(not LINUX, "linux only") def test_ionice_linux(self): + + def cleanup(init): + ioclass, value = init + if ioclass == psutil.IOPRIO_CLASS_NONE: + value = 0 + p.ionice(ioclass, value) + p = psutil.Process() if not CI_TESTING: self.assertEqual(p.ionice()[0], psutil.IOPRIO_CLASS_NONE) @@ -355,38 +362,33 @@ def test_ionice_linux(self): self.assertEqual(psutil.IOPRIO_CLASS_BE, 2) # normal self.assertEqual(psutil.IOPRIO_CLASS_IDLE, 3) # low init = p.ionice() + self.addCleanup(cleanup, init) + + # low + p.ionice(psutil.IOPRIO_CLASS_IDLE) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_IDLE, 0)) + with self.assertRaises(ValueError): # accepts no value + p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7) + # normal + p.ionice(psutil.IOPRIO_CLASS_BE) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 0)) + p.ionice(psutil.IOPRIO_CLASS_BE, value=7) + self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 7)) + with self.assertRaises(ValueError): + p.ionice(psutil.IOPRIO_CLASS_BE, value=8) try: - # low - p.ionice(psutil.IOPRIO_CLASS_IDLE) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_IDLE, 0)) - with self.assertRaises(ValueError): # accepts no value - p.ionice(psutil.IOPRIO_CLASS_IDLE, value=7) - # normal - p.ionice(psutil.IOPRIO_CLASS_BE) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 0)) - p.ionice(psutil.IOPRIO_CLASS_BE, value=7) - self.assertEqual(tuple(p.ionice()), (psutil.IOPRIO_CLASS_BE, 7)) - with self.assertRaises(ValueError): - p.ionice(psutil.IOPRIO_CLASS_BE, value=8) - try: - p.ionice(psutil.IOPRIO_CLASS_RT, value=7) - except psutil.AccessDenied: - pass - # errs - self.assertRaisesRegex( - ValueError, "ioclass accepts no value", - p.ionice, psutil.IOPRIO_CLASS_NONE, 1) - self.assertRaisesRegex( - ValueError, "ioclass accepts no value", - p.ionice, psutil.IOPRIO_CLASS_IDLE, 1) - self.assertRaisesRegex( - ValueError, "'ioclass' argument must be specified", - p.ionice, value=1) - finally: - ioclass, value = init - if ioclass == psutil.IOPRIO_CLASS_NONE: - value = 0 - p.ionice(ioclass, value) + p.ionice(psutil.IOPRIO_CLASS_RT, value=7) + except psutil.AccessDenied: + pass + # errs + with self.assertRaisesRegex(ValueError, "ioclass accepts no value"): + p.ionice(psutil.IOPRIO_CLASS_NONE, 1) + with self.assertRaisesRegex(ValueError, "ioclass accepts no value"): + p.ionice(psutil.IOPRIO_CLASS_IDLE, 1) + with self.assertRaisesRegex( + ValueError, "'ioclass' argument must be specified", + ): + p.ionice(value=1) @unittest.skipIf(not HAS_IONICE, "not supported") @unittest.skipIf(not WINDOWS, 'not supported on this win version') @@ -395,27 +397,26 @@ def test_ionice_win(self): if not CI_TESTING: self.assertEqual(p.ionice(), psutil.IOPRIO_NORMAL) init = p.ionice() + self.addCleanup(p.ionice, init) + + # base + p.ionice(psutil.IOPRIO_VERYLOW) + self.assertEqual(p.ionice(), psutil.IOPRIO_VERYLOW) + p.ionice(psutil.IOPRIO_LOW) + self.assertEqual(p.ionice(), psutil.IOPRIO_LOW) try: - # base - p.ionice(psutil.IOPRIO_VERYLOW) - self.assertEqual(p.ionice(), psutil.IOPRIO_VERYLOW) - p.ionice(psutil.IOPRIO_LOW) - self.assertEqual(p.ionice(), psutil.IOPRIO_LOW) - try: - p.ionice(psutil.IOPRIO_HIGH) - except psutil.AccessDenied: - pass - else: - self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH) - # errs - self.assertRaisesRegex( - TypeError, "value argument not accepted on Windows", - p.ionice, psutil.IOPRIO_NORMAL, value=1) - self.assertRaisesRegex( - ValueError, "is not a valid priority", - p.ionice, psutil.IOPRIO_HIGH + 1) - finally: - p.ionice(init) + p.ionice(psutil.IOPRIO_HIGH) + except psutil.AccessDenied: + pass + else: + self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH) + # errs + with self.assertRaisesRegex( + TypeError, "value argument not accepted on Windows", + ): + p.ionice(psutil.IOPRIO_NORMAL, value=1) + with self.assertRaisesRegex(ValueError, "is not a valid priority"): + p.ionice(psutil.IOPRIO_HIGH + 1) @unittest.skipIf(not HAS_RLIMIT, "not supported") def test_rlimit_get(self): @@ -821,59 +822,61 @@ def test_gids(self): self.assertEqual(os.getresgid(), p.gids()) def test_nice(self): + def cleanup(init): + try: + p.nice(init) + except psutil.AccessDenied: + pass + p = psutil.Process() self.assertRaises(TypeError, p.nice, "str") init = p.nice() - try: - if WINDOWS: - highest_prio = None - for prio in [psutil.IDLE_PRIORITY_CLASS, - psutil.BELOW_NORMAL_PRIORITY_CLASS, - psutil.NORMAL_PRIORITY_CLASS, - psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS]: - with self.subTest(prio=prio): - try: - p.nice(prio) - except psutil.AccessDenied: - pass + self.addCleanup(cleanup, init) + + if WINDOWS: + highest_prio = None + for prio in [psutil.IDLE_PRIORITY_CLASS, + psutil.BELOW_NORMAL_PRIORITY_CLASS, + psutil.NORMAL_PRIORITY_CLASS, + psutil.ABOVE_NORMAL_PRIORITY_CLASS, + psutil.HIGH_PRIORITY_CLASS, + psutil.REALTIME_PRIORITY_CLASS]: + with self.subTest(prio=prio): + try: + p.nice(prio) + except psutil.AccessDenied: + pass + else: + new_prio = p.nice() + # The OS may limit our maximum priority, + # even if the function succeeds. For higher + # priorities, we match either the expected + # value or the highest so far. + if prio in (psutil.ABOVE_NORMAL_PRIORITY_CLASS, + psutil.HIGH_PRIORITY_CLASS, + psutil.REALTIME_PRIORITY_CLASS): + if new_prio == prio or highest_prio is None: + highest_prio = prio + self.assertEqual(new_prio, highest_prio) else: - new_prio = p.nice() - # The OS may limit our maximum priority, - # even if the function succeeds. For higher - # priorities, we match either the expected - # value or the highest so far. - if prio in (psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS): - if new_prio == prio or highest_prio is None: - highest_prio = prio - self.assertEqual(new_prio, highest_prio) - else: - self.assertEqual(new_prio, prio) - else: - try: - if hasattr(os, "getpriority"): - self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), - p.nice()) - p.nice(1) - self.assertEqual(p.nice(), 1) - if hasattr(os, "getpriority"): - self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), - p.nice()) - # XXX - going back to previous nice value raises - # AccessDenied on MACOS - if not MACOS: - p.nice(0) - self.assertEqual(p.nice(), 0) - except psutil.AccessDenied: - pass - finally: + self.assertEqual(new_prio, prio) + else: try: - p.nice(init) + if hasattr(os, "getpriority"): + self.assertEqual( + os.getpriority(os.PRIO_PROCESS, os.getpid()), + p.nice()) + p.nice(1) + self.assertEqual(p.nice(), 1) + if hasattr(os, "getpriority"): + self.assertEqual( + os.getpriority(os.PRIO_PROCESS, os.getpid()), + p.nice()) + # XXX - going back to previous nice value raises + # AccessDenied on MACOS + if not MACOS: + p.nice(0) + self.assertEqual(p.nice(), 0) except psutil.AccessDenied: pass From c458816fc24474a8879918d80d097480b66d6818 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jan 2024 18:44:28 +0100 Subject: [PATCH 05/10] Adopt black formatting style (#2349) --- .github/workflows/build.yml | 2 +- .github/workflows/issues.py | 42 +- HISTORY.rst | 1 + Makefile | 14 +- docs/conf.py | 39 +- psutil/__init__.py | 111 +++- psutil/_common.py | 93 ++- psutil/_compat.py | 70 ++- psutil/_psaix.py | 90 ++- psutil/_psbsd.py | 120 +++- psutil/_pslinux.py | 368 +++++++---- psutil/_psosx.py | 46 +- psutil/_psposix.py | 32 +- psutil/_pssunos.py | 98 +-- psutil/_pswindows.py | 112 +++- psutil/tests/__init__.py | 245 +++++--- psutil/tests/__main__.py | 1 - psutil/tests/runner.py | 91 ++- psutil/tests/test_aix.py | 55 +- psutil/tests/test_bsd.py | 199 +++--- psutil/tests/test_connections.py | 145 +++-- psutil/tests/test_contracts.py | 83 ++- psutil/tests/test_linux.py | 628 +++++++++++-------- psutil/tests/test_memleaks.py | 19 +- psutil/tests/test_misc.py | 318 ++++++---- psutil/tests/test_osx.py | 34 +- psutil/tests/test_posix.py | 96 +-- psutil/tests/test_process.py | 248 +++++--- psutil/tests/test_sunos.py | 2 +- psutil/tests/test_system.py | 229 ++++--- psutil/tests/test_testutils.py | 47 +- psutil/tests/test_unicode.py | 23 +- psutil/tests/test_windows.py | 350 ++++++----- pyproject.toml | 7 + scripts/battery.py | 6 +- scripts/disk_usage.py | 9 +- scripts/free.py | 15 +- scripts/ifconfig.py | 36 +- scripts/internal/bench_oneshot.py | 12 +- scripts/internal/bench_oneshot_2.py | 6 +- scripts/internal/check_broken_links.py | 18 +- scripts/internal/clinter.py | 8 +- scripts/internal/download_wheels_appveyor.py | 12 +- scripts/internal/download_wheels_github.py | 10 +- scripts/internal/generate_manifest.py | 13 +- scripts/internal/git_pre_commit.py | 47 +- scripts/internal/print_access_denied.py | 7 +- scripts/internal/print_announce.py | 18 +- scripts/internal/print_api_speed.py | 34 +- scripts/internal/print_dist.py | 32 +- scripts/internal/print_downloads.py | 34 +- scripts/internal/print_hashes.py | 11 +- scripts/internal/print_timeline.py | 3 +- scripts/internal/winmake.py | 31 +- scripts/iotop.py | 9 +- scripts/netstat.py | 17 +- scripts/nettop.py | 13 +- scripts/pidof.py | 7 +- scripts/pmap.py | 8 +- scripts/procinfo.py | 44 +- scripts/procsmem.py | 6 +- scripts/ps.py | 33 +- scripts/sensors.py | 22 +- scripts/temperatures.py | 10 +- scripts/top.py | 86 ++- scripts/who.py | 5 +- scripts/winservices.py | 9 +- setup.py | 193 ++++-- 68 files changed, 3107 insertions(+), 1775 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f600b9d16..38c164e7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,7 +106,7 @@ jobs: python-version: 3.x - name: 'Run linters' run: | - python3 -m pip install ruff rstcheck toml-sort sphinx + python3 -m pip install ruff black rstcheck toml-sort sphinx make lint-all # Check sanity of .tar.gz + wheel files diff --git a/.github/workflows/issues.py b/.github/workflows/issues.py index b89def6ff..eddaa7f99 100755 --- a/.github/workflows/issues.py +++ b/.github/workflows/issues.py @@ -21,13 +21,14 @@ ROOT_DIR = os.path.realpath( - os.path.join(os.path.dirname(__file__), '..', '..')) + os.path.join(os.path.dirname(__file__), '..', '..') +) SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts') # --- constants - +# fmt: off LABELS_MAP = { # platforms "linux": [ @@ -94,13 +95,15 @@ ], } -LABELS_MAP['scripts'].extend( - [x for x in os.listdir(SCRIPTS_DIR) if x.endswith('.py')]) - OS_LABELS = [ "linux", "windows", "macos", "freebsd", "openbsd", "netbsd", "openbsd", "bsd", "sunos", "unix", "wsl", "aix", "cygwin", ] +# fmt: on + +LABELS_MAP['scripts'].extend( + [x for x in os.listdir(SCRIPTS_DIR) if x.endswith('.py')] +) ILLOGICAL_PAIRS = [ ('bug', 'enhancement'), @@ -247,10 +250,12 @@ def add_labels_from_new_body(issue, text): # add bug/enhancement label log("search for 'Bug fix: y/n' line") r = re.search(r"\* Bug fix:.*?\n", text) - if is_pr(issue) and \ - r is not None and \ - not has_label(issue, "bug") and \ - not has_label(issue, "enhancement"): + if ( + is_pr(issue) + and r is not None + and not has_label(issue, "bug") + and not has_label(issue, "enhancement") + ): log("found") s = r.group(0).lower() if 'yes' in s: @@ -289,20 +294,25 @@ def add_labels_from_new_body(issue, text): def on_new_issue(issue): def has_text(text): - return text in issue.title.lower() or \ - (issue.body and text in issue.body.lower()) + return text in issue.title.lower() or ( + issue.body and text in issue.body.lower() + ) def body_mentions_python_h(): if not issue.body: return False body = issue.body.replace(' ', '') - return "#include\n^~~~" in body or \ - "#include\r\n^~~~" in body + return ( + "#include\n^~~~" in body + or "#include\r\n^~~~" in body + ) log("searching for missing Python.h") - if has_text("missing python.h") or \ - has_text("python.h: no such file or directory") or \ - body_mentions_python_h(): + if ( + has_text("missing python.h") + or has_text("python.h: no such file or directory") + or body_mentions_python_h() + ): log("found mention of Python.h") issue.create_comment(REPLY_MISSING_PYTHON_HEADERS) issue.edit(state='closed') diff --git a/HISTORY.rst b/HISTORY.rst index 0af24c1c6..8467b90e5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,7 @@ 4 times faster. Before all connection types (TCP, UDP, UNIX) were retrieved internally, even if only a portion was returned. - 2342_, [NetBSD]: same as above (#2343) but for NetBSD. +- 2349_: adopted black formatting style. **Bug fixes** diff --git a/Makefile b/Makefile index 4e86550f6..6b6c3f32e 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ TSCRIPT = psutil/tests/runner.py # Internal. PY3_DEPS = \ + black \ check-manifest \ concurrencytest \ coverage \ @@ -18,6 +19,7 @@ PY3_DEPS = \ pypinfo \ requests \ rstcheck \ + ruff \ setuptools \ sphinx_rtd_theme \ teyit \ @@ -193,7 +195,10 @@ test-coverage: ## Run test coverage. # =================================================================== ruff: ## Run ruff linter. - @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --config=pyproject.toml --no-cache + @git ls-files '*.py' | xargs $(PYTHON) -m ruff check --no-cache + +black: ## Python files linting (via black) + @git ls-files '*.py' | xargs $(PYTHON) -m black --check --safe _pylint: ## Python pylint (not mandatory, just run it from time to time) @git ls-files '*.py' | xargs $(PYTHON) -m pylint --rcfile=pyproject.toml --jobs=${NUM_WORKERS} @@ -208,6 +213,7 @@ lint-toml: ## Linter for pyproject.toml @git ls-files '*.toml' | xargs toml-sort --check lint-all: ## Run all linters + ${MAKE} black ${MAKE} ruff ${MAKE} lint-c ${MAKE} lint-rst @@ -217,8 +223,11 @@ lint-all: ## Run all linters # Fixers # =================================================================== +fix-black: + git ls-files '*.py' | xargs $(PYTHON) -m black + fix-ruff: - @git ls-files '*.py' | xargs $(PYTHON) -m ruff --config=pyproject.toml --no-cache --fix + @git ls-files '*.py' | xargs $(PYTHON) -m ruff --no-cache --fix fix-unittests: ## Fix unittest idioms. @git ls-files '*test_*.py' | xargs $(PYTHON) -m teyit --show-stats @@ -228,6 +237,7 @@ fix-toml: ## Fix pyproject.toml fix-all: ## Run all code fixers. ${MAKE} fix-ruff + ${MAKE} fix-black ${MAKE} fix-unittests ${MAKE} fix-toml diff --git a/docs/conf.py b/docs/conf.py index 3adde46eb..f2b05483c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,11 +56,13 @@ def get_version(): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.imgmath', - 'sphinx.ext.viewcode', - 'sphinx.ext.intersphinx'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.imgmath', + 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -271,15 +273,12 @@ def get_version(): # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -289,8 +288,7 @@ def get_version(): # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'psutil.tex', 'psutil Documentation', - AUTHOR, 'manual'), + (master_doc, 'psutil.tex', 'psutil Documentation', AUTHOR, 'manual') ] # The name of an image file (relative to this directory) to place at the top of @@ -330,10 +328,7 @@ def get_version(): # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'psutil', 'psutil Documentation', - [author], 1), -] +man_pages = [(master_doc, 'psutil', 'psutil Documentation', [author], 1)] # If true, show URL addresses after external links. # @@ -345,11 +340,15 @@ def get_version(): # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'psutil', 'psutil Documentation', - author, 'psutil', 'One line description of project.', - 'Miscellaneous'), -] +texinfo_documents = [( + master_doc, + 'psutil', + 'psutil Documentation', + author, + 'psutil', + 'One line description of project.', + 'Miscellaneous', +)] # Documents to append as an appendix to all manuals. # @@ -373,5 +372,5 @@ def get_version(): 'https://media.readthedocs.org/css/sphinx_rtd_theme.css', 'https://media.readthedocs.org/css/readthedocs-doc-embed.css', '_static/css/custom.css', - ], + ] } diff --git a/psutil/__init__.py b/psutil/__init__.py index 3e12b5de2..8138db41e 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -145,6 +145,7 @@ raise NotImplementedError('platform %s is not supported' % sys.platform) +# fmt: off __all__ = [ # exceptions "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied", @@ -191,6 +192,7 @@ # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors "users", "boot_time", # others ] +# fmt: on __all__.extend(_psplatform.__extra__all__) @@ -225,18 +227,25 @@ # was compiled for a different version of psutil. # We want to prevent that by failing sooner rather than later. # See: https://github.com/giampaolo/psutil/issues/564 -if (int(__version__.replace('.', '')) != - getattr(_psplatform.cext, 'version', None)): +if int(__version__.replace('.', '')) != getattr( + _psplatform.cext, 'version', None +): msg = "version conflict: %r C extension " % _psplatform.cext.__file__ msg += "module was built for another version of psutil" if hasattr(_psplatform.cext, 'version'): msg += " (%s instead of %s)" % ( - '.'.join([x for x in str(_psplatform.cext.version)]), __version__) + '.'.join([x for x in str(_psplatform.cext.version)]), + __version__, + ) else: msg += " (different than %s)" % __version__ msg += "; you may try to 'pip uninstall psutil', manually remove %s" % ( - getattr(_psplatform.cext, "__file__", - "the existing psutil install directory")) + getattr( + _psplatform.cext, + "__file__", + "the existing psutil install directory", + ) + ) msg += " or clean the virtual env somehow, then reinstall" raise ImportError(msg) @@ -250,6 +259,7 @@ # Faster version (Windows and Linux). _ppid_map = _psplatform.ppid_map else: # pragma: no cover + def _ppid_map(): """Return a {pid: ppid, ...} dict for all running processes in one shot. Used to speed up Process.children(). @@ -390,7 +400,8 @@ def __str__(self): return "%s.%s(%s)" % ( self.__class__.__module__, self.__class__.__name__, - ", ".join(["%s=%r" % (k, v) for k, v in info.items()])) + ", ".join(["%s=%r" % (k, v) for k, v in info.items()]), + ) __repr__ = __str__ @@ -666,6 +677,7 @@ def exe(self): May also be an empty string. The return value is cached after first call. """ + def guess_it(fallback): # try to guess exe from cmdline[0] in absence of a native # exe representation @@ -675,9 +687,11 @@ def guess_it(fallback): # Attempt to guess only in case of an absolute path. # It is not safe otherwise as the process might have # changed cwd. - if (os.path.isabs(exe) and - os.path.isfile(exe) and - os.access(exe, os.X_OK)): + if ( + os.path.isabs(exe) + and os.path.isfile(exe) + and os.access(exe, os.X_OK) + ): return exe if isinstance(fallback, AccessDenied): raise fallback @@ -1042,7 +1056,7 @@ def timer(): # This is the utilization split evenly between all CPUs. # E.g. a busy loop process on a 2-CPU-cores system at this # point is reported as 50% instead of 100%. - overall_cpus_percent = ((delta_proc / delta_time) * 100) + overall_cpus_percent = (delta_proc / delta_time) * 100 except ZeroDivisionError: # interval was too low return 0.0 @@ -1120,11 +1134,15 @@ def memory_percent(self, memtype="rss"): valid_types = list(_psplatform.pfullmem._fields) if memtype not in valid_types: msg = "invalid memtype %r; valid types are %r" % ( - memtype, tuple(valid_types), + memtype, + tuple(valid_types), ) raise ValueError(msg) - fun = self.memory_info if memtype in _psplatform.pmem._fields else \ - self.memory_full_info + fun = ( + self.memory_info + if memtype in _psplatform.pmem._fields + else self.memory_full_info + ) metrics = fun() value = getattr(metrics, memtype) @@ -1140,6 +1158,7 @@ def memory_percent(self, memtype="rss"): return (value / float(total_phymem)) * 100 if hasattr(_psplatform.Process, "memory_maps"): + def memory_maps(self, grouped=True): """Return process' mapped memory regions as a list of namedtuples whose fields are variable depending on the platform. @@ -1201,6 +1220,7 @@ def connections(self, kind='inet'): # --- signals if POSIX: + def _send_signal(self, sig): assert not self.pid < 0, self.pid self._raise_if_pid_reused() @@ -1307,11 +1327,13 @@ def wait(self, timeout=None): # The valid attr names which can be processed by Process.as_dict(). +# fmt: off _as_dict_attrnames = set( [x for x in dir(Process) if not x.startswith('_') and x not in {'send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', 'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit', 'memory_info_ex', 'oneshot'}]) +# fmt: on # ===================================================================== @@ -1388,7 +1410,8 @@ def __getattribute__(self, name): return object.__getattribute__(self.__subproc, name) except AttributeError: msg = "%s instance has no attribute '%s'" % ( - self.__class__.__name__, name, + self.__class__.__name__, + name, ) raise AttributeError(msg) @@ -1487,7 +1510,8 @@ def remove(pid): if proc.is_running(): if attrs is not None: proc.info = proc.as_dict( - attrs=attrs, ad_value=ad_value) + attrs=attrs, ad_value=ad_value + ) yield proc else: yield add(pid) @@ -1546,6 +1570,7 @@ def wait_procs(procs, timeout=None, callback=None): >>> for p in alive: ... p.kill() """ + def check_gone(proc, timeout): try: returncode = proc.wait(timeout=timeout) @@ -1666,7 +1691,7 @@ def cpu_times(percpu=False): try: _last_per_cpu_times = { - threading.current_thread().ident: cpu_times(percpu=True), + threading.current_thread().ident: cpu_times(percpu=True) } except Exception: # noqa: BLE001 # Don't want to crash at import time. @@ -2033,6 +2058,7 @@ def disk_partitions(all=False): If *all* parameter is False return physical devices only and ignore all others. """ + def pathconf(path, name): try: return os.pathconf(path, name) @@ -2045,7 +2071,8 @@ def pathconf(path, name): for item in ret: nt = item._replace( maxfile=pathconf(item.mountpoint, 'PC_NAME_MAX'), - maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX')) + maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX'), + ) new.append(nt) return new else: @@ -2100,7 +2127,8 @@ def disk_io_counters(perdisk=False, nowrap=True): disk_io_counters.cache_clear = functools.partial( - _wrap_numbers.cache_clear, 'psutil.disk_io_counters') + _wrap_numbers.cache_clear, 'psutil.disk_io_counters' +) disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" @@ -2149,7 +2177,8 @@ def net_io_counters(pernic=False, nowrap=True): net_io_counters.cache_clear = functools.partial( - _wrap_numbers.cache_clear, 'psutil.net_io_counters') + _wrap_numbers.cache_clear, 'psutil.net_io_counters' +) net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache" @@ -2213,8 +2242,10 @@ def net_if_addrs(): except ValueError: if WINDOWS and fam == -1: fam = _psplatform.AF_LINK - elif (hasattr(_psplatform, "AF_LINK") and - fam == _psplatform.AF_LINK): + elif ( + hasattr(_psplatform, "AF_LINK") + and fam == _psplatform.AF_LINK + ): # Linux defines AF_LINK as an alias for AF_PACKET. # We re-set the family here so that repr(family) # will show AF_LINK rather than AF_PACKET @@ -2261,6 +2292,7 @@ def sensors_temperatures(fahrenheit=False): All temperatures are expressed in celsius unless *fahrenheit* is set to True. """ + def convert(n): if n is not None: return (float(n) * 9 / 5) + 32 if fahrenheit else n @@ -2281,7 +2313,8 @@ def convert(n): high = critical ret[name].append( - _common.shwtemp(label, current, high, critical)) + _common.shwtemp(label, current, high, critical) + ) return dict(ret) @@ -2372,6 +2405,7 @@ def _set_debug(value): messages to stderr. """ import psutil._common + psutil._common.PSUTIL_DEBUG = bool(value) _psplatform.cext.set_debug(bool(value)) @@ -2381,11 +2415,13 @@ def test(): # pragma: no cover from ._compat import get_terminal_size today_day = datetime.date.today() + # fmt: off templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s %s" attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times', 'create_time', 'memory_info', 'status', 'nice', 'username'] print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE", # NOQA "STATUS", "START", "TIME", "CMDLINE")) + # fmt: on for p in process_iter(attrs, ad_value=None): if p.info['create_time']: ctime = datetime.datetime.fromtimestamp(p.info['create_time']) @@ -2396,8 +2432,9 @@ def test(): # pragma: no cover else: ctime = '' if p.info['cpu_times']: - cputime = time.strftime("%M:%S", - time.localtime(sum(p.info['cpu_times']))) + cputime = time.strftime( + "%M:%S", time.localtime(sum(p.info['cpu_times'])) + ) else: cputime = '' @@ -2410,12 +2447,21 @@ def test(): # pragma: no cover if user and WINDOWS and '\\' in user: user = user.split('\\')[1] user = user[:9] - vms = bytes2human(p.info['memory_info'].vms) if \ - p.info['memory_info'] is not None else '' - rss = bytes2human(p.info['memory_info'].rss) if \ - p.info['memory_info'] is not None else '' - memp = round(p.info['memory_percent'], 1) if \ - p.info['memory_percent'] is not None else '' + vms = ( + bytes2human(p.info['memory_info'].vms) + if p.info['memory_info'] is not None + else '' + ) + rss = ( + bytes2human(p.info['memory_info'].rss) + if p.info['memory_info'] is not None + else '' + ) + memp = ( + round(p.info['memory_percent'], 1) + if p.info['memory_percent'] is not None + else '' + ) nice = int(p.info['nice']) if p.info['nice'] else '' if p.info['cmdline']: cmdline = ' '.join(p.info['cmdline']) @@ -2433,8 +2479,9 @@ def test(): # pragma: no cover status, ctime, cputime, - cmdline) - print(line[:get_terminal_size()[0]]) # NOQA + cmdline, + ) + print(line[: get_terminal_size()[0]]) # NOQA del memoize_when_activated, division diff --git a/psutil/_common.py b/psutil/_common.py index 95323aabc..6989feafd 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -47,6 +47,7 @@ PSUTIL_DEBUG = bool(os.getenv('PSUTIL_DEBUG')) _DEFAULT = object() +# fmt: off __all__ = [ # OS constants 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', @@ -77,6 +78,7 @@ # shell utils 'hilite', 'term_supports_colors', 'print_color', ] +# fmt: on # =================================================================== @@ -138,6 +140,7 @@ NIC_DUPLEX_HALF = 1 NIC_DUPLEX_UNKNOWN = 0 else: + class NicDuplex(enum.IntEnum): NIC_DUPLEX_FULL = 2 NIC_DUPLEX_HALF = 1 @@ -150,6 +153,7 @@ class NicDuplex(enum.IntEnum): POWER_TIME_UNKNOWN = -1 POWER_TIME_UNLIMITED = -2 else: + class BatteryTime(enum.IntEnum): POWER_TIME_UNKNOWN = -1 POWER_TIME_UNLIMITED = -2 @@ -174,6 +178,7 @@ class BatteryTime(enum.IntEnum): # --- for system functions +# fmt: off # psutil.swap_memory() sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin', 'sout']) @@ -214,12 +219,14 @@ class BatteryTime(enum.IntEnum): sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged']) # psutil.sensors_fans() sfan = namedtuple('sfan', ['label', 'current']) +# fmt: on # --- for Process methods # psutil.Process.cpu_times() -pcputimes = namedtuple('pcputimes', - ['user', 'system', 'children_user', 'children_system']) +pcputimes = namedtuple( + 'pcputimes', ['user', 'system', 'children_user', 'children_system'] +) # psutil.Process.open_files() popenfile = namedtuple('popenfile', ['path', 'fd']) # psutil.Process.threads() @@ -229,15 +236,17 @@ class BatteryTime(enum.IntEnum): # psutil.Process.gids() pgids = namedtuple('pgids', ['real', 'effective', 'saved']) # psutil.Process.io_counters() -pio = namedtuple('pio', ['read_count', 'write_count', - 'read_bytes', 'write_bytes']) +pio = namedtuple( + 'pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes'] +) # psutil.Process.ionice() pionice = namedtuple('pionice', ['ioclass', 'value']) # psutil.Process.ctx_switches() pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary']) # psutil.Process.connections() -pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr', - 'status']) +pconn = namedtuple( + 'pconn', ['fd', 'family', 'type', 'laddr', 'raddr', 'status'] +) # psutil.connections() and psutil.Process.connections() addr = namedtuple('addr', ['ip', 'port']) @@ -266,9 +275,7 @@ class BatteryTime(enum.IntEnum): }) if AF_UNIX is not None: - conn_tmap.update({ - "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), - }) + conn_tmap.update({"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM])}) # ===================================================================== @@ -298,7 +305,8 @@ def __str__(self): info = self._infodict(("pid", "ppid", "name")) if info: details = "(%s)" % ", ".join( - ["%s=%r" % (k, v) for k, v in info.items()]) + ["%s=%r" % (k, v) for k, v in info.items()] + ) else: details = None return " ".join([x for x in (getattr(self, "msg", ""), details) if x]) @@ -387,6 +395,7 @@ def __init__(self, seconds, pid=None, name=None): value = None """) else: + def raise_from(value, from_value): raise value @@ -426,6 +435,7 @@ def memoize(fun): It does NOT support: - methods """ + @functools.wraps(fun) def wrapper(*args, **kwargs): key = (args, frozenset(sorted(kwargs.items()))) @@ -473,6 +483,7 @@ def memoize_when_activated(fun): >>> foo() >>> """ + @functools.wraps(fun) def wrapper(self): try: @@ -579,7 +590,7 @@ def parse_environ_block(data): equal_pos = data.find("=", pos, next_pos) if equal_pos > pos: key = data[pos:equal_pos] - value = data[equal_pos + 1:next_pos] + value = data[equal_pos + 1 : next_pos] # Windows expects environment variables to be uppercase only if WINDOWS_: key = key.upper() @@ -638,9 +649,12 @@ def deprecated_method(replacement): """A decorator which can be used to mark a method as deprecated 'replcement' is the method name which will be called instead. """ + def outer(fun): msg = "%s() is deprecated and will be removed; use %s() instead" % ( - fun.__name__, replacement) + fun.__name__, + replacement, + ) if fun.__doc__ is None: fun.__doc__ = msg @@ -648,7 +662,9 @@ def outer(fun): def inner(self, *args, **kwargs): warnings.warn(msg, category=DeprecationWarning, stacklevel=2) return getattr(self, replacement)(*args, **kwargs) + return inner + return outer @@ -783,8 +799,12 @@ def open_text(fname): # See: # https://github.com/giampaolo/psutil/issues/675 # https://github.com/giampaolo/psutil/pull/733 - fobj = open(fname, buffering=FILE_READ_BUFFER_SIZE, - encoding=ENCODING, errors=ENCODING_ERRS) + fobj = open( + fname, + buffering=FILE_READ_BUFFER_SIZE, + encoding=ENCODING, + errors=ENCODING_ERRS, + ) try: # Dictates per-line read(2) buffer size. Defaults is 8k. See: # https://github.com/giampaolo/psutil/issues/2050#issuecomment-1013387546 @@ -845,9 +865,12 @@ def get_procfs_path(): if PY3: + def decode(s): return s.decode(encoding=ENCODING, errors=ENCODING_ERRS) + else: + def decode(s): return s @@ -863,6 +886,7 @@ def term_supports_colors(file=sys.stdout): # pragma: no cover return True try: import curses + assert file.isatty() curses.setupterm() assert curses.tigetnum("colors") > 0 @@ -877,14 +901,24 @@ def hilite(s, color=None, bold=False): # pragma: no cover if not term_supports_colors(): return s attr = [] - colors = dict(green='32', red='91', brown='33', yellow='93', blue='34', - violet='35', lightblue='36', grey='37', darkgrey='30') + colors = dict( + blue='34', + brown='33', + darkgrey='30', + green='32', + grey='37', + lightblue='36', + red='91', + violet='35', + yellow='93', + ) colors[None] = '29' try: color = colors[color] except KeyError: - raise ValueError("invalid color %r; choose between %s" % ( - list(colors.keys()))) + raise ValueError( + "invalid color %r; choose between %s" % (list(colors.keys())) + ) attr.append(color) if bold: attr.append('1') @@ -892,7 +926,8 @@ def hilite(s, color=None, bold=False): # pragma: no cover def print_color( - s, color=None, bold=False, file=sys.stdout): # pragma: no cover + s, color=None, bold=False, file=sys.stdout +): # pragma: no cover """Print a colorized version of string.""" if not term_supports_colors(): print(s, file=file) # NOQA @@ -903,16 +938,19 @@ def print_color( DEFAULT_COLOR = 7 GetStdHandle = ctypes.windll.Kernel32.GetStdHandle - SetConsoleTextAttribute = \ + SetConsoleTextAttribute = ( ctypes.windll.Kernel32.SetConsoleTextAttribute + ) colors = dict(green=2, red=4, brown=6, yellow=6) colors[None] = DEFAULT_COLOR try: color = colors[color] except KeyError: - raise ValueError("invalid color %r; choose between %r" % ( - color, list(colors.keys()))) + raise ValueError( + "invalid color %r; choose between %r" + % (color, list(colors.keys())) + ) if bold and color <= 7: color += 8 @@ -921,7 +959,7 @@ def print_color( handle = GetStdHandle(handle_id) SetConsoleTextAttribute(handle, color) try: - print(s, file=file) # NOQA + print(s, file=file) # NOQA finally: SetConsoleTextAttribute(handle, DEFAULT_COLOR) @@ -930,13 +968,16 @@ def debug(msg): """If PSUTIL_DEBUG env var is set, print a debug message to stderr.""" if PSUTIL_DEBUG: import inspect + fname, lineno, _, lines, index = inspect.getframeinfo( - inspect.currentframe().f_back) + inspect.currentframe().f_back + ) if isinstance(msg, Exception): if isinstance(msg, (OSError, IOError, EnvironmentError)): # ...because str(exc) may contain info about the file name msg = "ignoring %s" % msg else: msg = "ignoring %r" % msg - print("psutil-debug [%s:%s]> %s" % (fname, lineno, msg), # NOQA - file=sys.stderr) + print( # noqa + "psutil-debug [%s:%s]> %s" % (fname, lineno, msg), file=sys.stderr + ) diff --git a/psutil/_compat.py b/psutil/_compat.py index 4957b22fd..3db56c601 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -16,6 +16,7 @@ import types +# fmt: off __all__ = [ # constants "PY3", @@ -31,7 +32,9 @@ "redirect_stderr", # python 3 exceptions "FileNotFoundError", "PermissionError", "ProcessLookupError", - "InterruptedError", "ChildProcessError", "FileExistsError"] + "InterruptedError", "ChildProcessError", "FileExistsError", +] +# fmt: on PY3 = sys.version_info[0] >= 3 @@ -49,6 +52,7 @@ def u(s): def b(s): return s.encode("latin-1") + else: long = long range = xrange @@ -147,7 +151,6 @@ def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): def _instance_checking_exception(base_exception=Exception): def wrapped(instance_checker): class TemporaryClass(base_exception): - def __init__(self, *args, **kwargs): if len(args) == 1 and isinstance(args[0], TemporaryClass): unwrap_me = args[0] @@ -155,7 +158,9 @@ def __init__(self, *args, **kwargs): if not attr.startswith('__'): setattr(self, attr, getattr(unwrap_me, attr)) else: - super(TemporaryClass, self).__init__(*args, **kwargs) # noqa + super(TemporaryClass, self).__init__( # noqa + *args, **kwargs + ) class __metaclass__(type): def __instancecheck__(cls, inst): @@ -181,8 +186,7 @@ def ProcessLookupError(inst): @_instance_checking_exception(EnvironmentError) def PermissionError(inst): - return getattr(inst, 'errno', _SENTINEL) in ( - errno.EACCES, errno.EPERM) + return getattr(inst, 'errno', _SENTINEL) in (errno.EACCES, errno.EPERM) @_instance_checking_exception(EnvironmentError) def InterruptedError(inst): @@ -202,8 +206,10 @@ def FileExistsError(inst): except FileExistsError: pass except OSError: - msg = ("broken or incompatible Python implementation, see: " - "https://github.com/giampaolo/psutil/issues/1659") + msg = ( + "broken or incompatible Python implementation, see: " + "https://github.com/giampaolo/psutil/issues/1659" + ) raise RuntimeError(msg) @@ -222,10 +228,11 @@ def FileExistsError(inst): from dummy_threading import RLock _CacheInfo = collections.namedtuple( - "CacheInfo", ["hits", "misses", "maxsize", "currsize"]) + "CacheInfo", ["hits", "misses", "maxsize", "currsize"] + ) class _HashedSeq(list): - __slots__ = ('hashvalue', ) + __slots__ = ('hashvalue',) def __init__(self, tup, hash=hash): self[:] = tup @@ -234,10 +241,17 @@ def __init__(self, tup, hash=hash): def __hash__(self): return self.hashvalue - def _make_key(args, kwds, typed, - kwd_mark=(_SENTINEL, ), - fasttypes=set((int, str, frozenset, type(None))), # noqa - sorted=sorted, tuple=tuple, type=type, len=len): + def _make_key( + args, + kwds, + typed, + kwd_mark=(_SENTINEL,), + fasttypes=set((int, str, frozenset, type(None))), # noqa + sorted=sorted, + tuple=tuple, + type=type, + len=len, + ): key = args if kwds: sorted_items = sorted(kwds.items()) @@ -256,6 +270,7 @@ def lru_cache(maxsize=100, typed=False): """Least-recently-used cache decorator, see: http://docs.python.org/3/library/functools.html#functools.lru_cache. """ + def decorating_function(user_function): cache = {} stats = [0, 0] @@ -269,11 +284,14 @@ def decorating_function(user_function): nonlocal_root = [root] PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 if maxsize == 0: + def wrapper(*args, **kwds): result = user_function(*args, **kwds) stats[MISSES] += 1 return result + elif maxsize is None: + def wrapper(*args, **kwds): key = make_key(args, kwds, typed) result = cache_get(key, root) @@ -284,7 +302,9 @@ def wrapper(*args, **kwds): cache[key] = result stats[MISSES] += 1 return result + else: + def wrapper(*args, **kwds): if kwds or typed: key = make_key(args, kwds, typed) @@ -294,7 +314,7 @@ def wrapper(*args, **kwds): try: link = cache_get(key) if link is not None: - root, = nonlocal_root + (root,) = nonlocal_root link_prev, link_next, key, result = link link_prev[NEXT] = link_next link_next[PREV] = link_prev @@ -309,7 +329,7 @@ def wrapper(*args, **kwds): result = user_function(*args, **kwds) lock.acquire() try: - root, = nonlocal_root + (root,) = nonlocal_root if key in cache: pass elif _len(cache) >= maxsize: @@ -334,8 +354,9 @@ def cache_info(): """Report cache statistics.""" lock.acquire() try: - return _CacheInfo(stats[HITS], stats[MISSES], maxsize, - len(cache)) + return _CacheInfo( + stats[HITS], stats[MISSES], maxsize, len(cache) + ) finally: lock.release() @@ -362,6 +383,7 @@ def cache_clear(): try: from shutil import which except ImportError: + def which(cmd, mode=os.F_OK | os.X_OK, path=None): """Given a command, mode, and a PATH string, return the path which conforms to the given mode on the PATH, or None if there is no such @@ -371,9 +393,13 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None): of os.environ.get("PATH"), or can be overridden with a custom search path. """ + def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) and - not os.path.isdir(fn)) + return ( + os.path.exists(fn) + and os.access(fn, mode) + and not os.path.isdir(fn) + ) if os.path.dirname(cmd): if _access_check(cmd, mode): @@ -414,6 +440,7 @@ def _access_check(fn, mode): try: from shutil import get_terminal_size except ImportError: + def get_terminal_size(fallback=(80, 24)): try: import fcntl @@ -425,7 +452,8 @@ def get_terminal_size(fallback=(80, 24)): try: # This should work on Linux. res = struct.unpack( - 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234')) + 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234') + ) return (res[1], res[0]) except Exception: # noqa: BLE001 return fallback @@ -435,6 +463,7 @@ def get_terminal_size(fallback=(80, 24)): try: from subprocess import TimeoutExpired as SubprocessTimeoutExpired except ImportError: + class SubprocessTimeoutExpired(Exception): pass @@ -443,6 +472,7 @@ class SubprocessTimeoutExpired(Exception): try: from contextlib import redirect_stderr except ImportError: + @contextlib.contextmanager def redirect_stderr(new_target): original = sys.stderr diff --git a/psutil/_psaix.py b/psutil/_psaix.py index 6c2962c5e..7310ab6c3 100644 --- a/psutil/_psaix.py +++ b/psutil/_psaix.py @@ -53,7 +53,7 @@ cext.SIDL: _common.STATUS_IDLE, cext.SZOMB: _common.STATUS_ZOMBIE, cext.SACTIVE: _common.STATUS_RUNNING, - cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this? + cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this? cext.SSTOP: _common.STATUS_STOPPED, } @@ -80,7 +80,8 @@ nice=4, num_threads=5, status=6, - ttynr=7) + ttynr=7, +) # ===================================================================== @@ -148,8 +149,9 @@ def cpu_count_cores(): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if PY3: - stdout, stderr = (x.decode(sys.stdout.encoding) - for x in (stdout, stderr)) + stdout, stderr = ( + x.decode(sys.stdout.encoding) for x in (stdout, stderr) + ) if p.returncode != 0: raise RuntimeError("%r command error\n%s" % (cmd, stderr)) processors = stdout.strip().splitlines() @@ -160,7 +162,8 @@ def cpu_stats(): """Return various CPU stats as a named tuple.""" ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats() return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls) + ctx_switches, interrupts, soft_interrupts, syscalls + ) # ===================================================================== @@ -189,8 +192,9 @@ def disk_partitions(all=False): if not disk_usage(mountpoint).total: continue maxfile = maxpath = None # set later - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, - maxfile, maxpath) + ntuple = _common.sdiskpart( + device, mountpoint, fstype, opts, maxfile, maxpath + ) retlist.append(ntuple) return retlist @@ -212,8 +216,10 @@ def net_connections(kind, _pid=-1): """ cmap = _common.conn_tmap if kind not in cmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in cmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in cmap])) + ) families, types = _common.conn_tmap[kind] rawlist = cext.net_connections(_pid) ret = [] @@ -223,16 +229,23 @@ def net_connections(kind, _pid=-1): continue if type_ not in types: continue - nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status, - TCP_STATUSES, pid=pid if _pid == -1 else None) + nt = conn_to_ntuple( + fd, + fam, + type_, + laddr, + raddr, + status, + TCP_STATUSES, + pid=pid if _pid == -1 else None, + ) ret.append(nt) return ret def net_if_stats(): """Get NIC stats (isup, duplex, speed, mtu).""" - duplex_map = {"Full": NIC_DUPLEX_FULL, - "Half": NIC_DUPLEX_HALF} + duplex_map = {"Full": NIC_DUPLEX_FULL, "Half": NIC_DUPLEX_HALF} names = set([x[0] for x in net_if_addrs()]) ret = {} for name in names: @@ -244,15 +257,20 @@ def net_if_stats(): # looks like it is using an undocumented ioctl?) duplex = "" speed = 0 - p = subprocess.Popen(["/usr/bin/entstat", "-d", name], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + ["/usr/bin/entstat", "-d", name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = p.communicate() if PY3: - stdout, stderr = (x.decode(sys.stdout.encoding) - for x in (stdout, stderr)) + stdout, stderr = ( + x.decode(sys.stdout.encoding) for x in (stdout, stderr) + ) if p.returncode == 0: re_result = re.search( - r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout) + r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout + ) if re_result is not None: speed = int(re_result.group(1)) duplex = re_result.group(2) @@ -312,6 +330,7 @@ def wrap_exceptions(fun): """Call callable into a try/except clause and translate ENOENT, EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: @@ -326,6 +345,7 @@ def wrapper(self, *args, **kwargs): raise ZombieProcess(self.pid, self._name, self._ppid) except PermissionError: raise AccessDenied(self.pid, self._name) + return wrapper @@ -378,17 +398,20 @@ def exe(self): if not os.path.isabs(exe): # if cwd has changed, we're out of luck - this may be wrong! exe = os.path.abspath(os.path.join(self.cwd(), exe)) - if (os.path.isabs(exe) and - os.path.isfile(exe) and - os.access(exe, os.X_OK)): + if ( + os.path.isabs(exe) + and os.path.isfile(exe) + and os.access(exe, os.X_OK) + ): return exe # not found, move to search in PATH using basename only exe = os.path.basename(exe) # search for exe name PATH for path in os.environ["PATH"].split(":"): possible_exe = os.path.abspath(os.path.join(path, exe)) - if (os.path.isfile(possible_exe) and - os.access(possible_exe, os.X_OK)): + if os.path.isfile(possible_exe) and os.access( + possible_exe, os.X_OK + ): return possible_exe return '' @@ -409,6 +432,7 @@ def num_threads(self): return self._proc_basic_info()[proc_info_map['num_threads']] if HAS_THREADS: + @wrap_exceptions def threads(self): rawlist = cext.proc_threads(self.pid) @@ -471,7 +495,7 @@ def cpu_times(self): def terminal(self): ttydev = self._proc_basic_info()[proc_info_map['ttynr']] # convert from 64-bit dev_t to 32-bit dev_t and then map the device - ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF)) + ttydev = ((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF) # try to match rdev of /dev/pts/* files ttydev for dev in glob.glob("/dev/**/*"): if os.stat(dev).st_rdev == ttydev: @@ -506,12 +530,16 @@ def status(self): def open_files(self): # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then # find matching name of the inode) - p = subprocess.Popen(["/usr/bin/procfiles", "-n", str(self.pid)], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + ["/usr/bin/procfiles", "-n", str(self.pid)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = p.communicate() if PY3: - stdout, stderr = (x.decode(sys.stdout.encoding) - for x in (stdout, stderr)) + stdout, stderr = ( + x.decode(sys.stdout.encoding) for x in (stdout, stderr) + ) if "no such process" in stderr.lower(): raise NoSuchProcess(self.pid, self._name) procfiles = re.findall(r"(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout) @@ -527,20 +555,20 @@ def open_files(self): @wrap_exceptions def num_fds(self): - if self.pid == 0: # no /proc/0/fd + if self.pid == 0: # no /proc/0/fd return 0 return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))) @wrap_exceptions def num_ctx_switches(self): - return _common.pctxsw( - *cext.proc_num_ctx_switches(self.pid)) + return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid)) @wrap_exceptions def wait(self, timeout=None): return _psposix.wait_pid(self.pid, timeout, self._name) if HAS_PROC_IO_COUNTERS: + @wrap_exceptions def io_counters(self): try: diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index fc03d1114..da68f5efd 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -141,6 +141,7 @@ # ===================================================================== +# fmt: off # psutil.virtual_memory() svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', @@ -170,6 +171,7 @@ else: sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', 'read_bytes', 'write_bytes']) +# fmt: on # ===================================================================== @@ -209,8 +211,19 @@ def virtual_memory(): used = active + wired + cached percent = usage_percent((total - avail), total, round_=1) - return svmem(total, avail, percent, used, free, - active, inactive, buffers, cached, shared, wired) + return svmem( + total, + avail, + percent, + used, + free, + active, + inactive, + buffers, + cached, + shared, + wired, + ) def swap_memory(): @@ -232,6 +245,7 @@ def cpu_times(): if HAS_PER_CPU_TIMES: + def per_cpu_times(): """Return system CPU times as a namedtuple.""" ret = [] @@ -240,6 +254,7 @@ def per_cpu_times(): item = scputimes(user, nice, system, idle, irq) ret.append(item) return ret + else: # XXX # Ok, this is very dirty. @@ -267,10 +282,13 @@ def cpu_count_logical(): if OPENBSD or NETBSD: + def cpu_count_cores(): # OpenBSD and NetBSD do not implement this. return 1 if cpu_count_logical() == 1 else None + else: + def cpu_count_cores(): """Return the number of CPU cores in the system.""" # From the C module we'll get an XML string similar to this: @@ -284,7 +302,7 @@ def cpu_count_cores(): # get rid of padding chars appended at the end of the string index = s.rfind("") if index != -1: - s = s[:index + 9] + s = s[: index + 9] root = ElementTree.fromstring(s) try: ret = len(root.findall('group/children/group/cpu')) or None @@ -314,8 +332,9 @@ def cpu_stats(): # # Note: the C ext is returning some metrics we are not exposing: # traps, faults and forks. - ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \ + ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = ( cext.cpu_stats() + ) with open('/proc/stat', 'rb') as f: for line in f: if line.startswith(b'intr'): @@ -323,12 +342,14 @@ def cpu_stats(): elif OPENBSD: # Note: the C ext is returning some metrics we are not exposing: # traps, faults and forks. - ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = \ + ctxsw, intrs, soft_intrs, syscalls, traps, faults, forks = ( cext.cpu_stats() + ) return _common.scpustats(ctxsw, intrs, soft_intrs, syscalls) if FREEBSD: + def cpu_freq(): """Return frequency metrics for CPUs. As of Dec 2018 only CPU 0 appears to be supported by FreeBSD and all other cores @@ -352,7 +373,9 @@ def cpu_freq(): max_freq = None ret.append(_common.scpufreq(current, min_freq, max_freq)) return ret + elif OPENBSD: + def cpu_freq(): curr = float(cext.cpu_freq()) return [_common.scpufreq(curr, 0.0, 0.0)] @@ -373,8 +396,9 @@ def disk_partitions(all=False): for partition in partitions: device, mountpoint, fstype, opts = partition maxfile = maxpath = None # set later - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, - maxfile, maxpath) + ntuple = _common.sdiskpart( + device, mountpoint, fstype, opts, maxfile, maxpath + ) retlist.append(ntuple) return retlist @@ -410,16 +434,19 @@ def net_if_stats(): duplex = _common.NicDuplex(duplex) output_flags = ','.join(flags) isup = 'running' in flags - ret[name] = _common.snicstats(isup, duplex, speed, mtu, - output_flags) + ret[name] = _common.snicstats( + isup, duplex, speed, mtu, output_flags + ) return ret def net_connections(kind): """System-wide network connections.""" if kind not in _common.conn_tmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in conn_tmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap])) + ) families, types = conn_tmap[kind] ret = set() @@ -432,8 +459,9 @@ def net_connections(kind): for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - nt = conn_to_ntuple(fd, fam, type, laddr, raddr, - status, TCP_STATUSES, pid) + nt = conn_to_ntuple( + fd, fam, type, laddr, raddr, status, TCP_STATUSES, pid + ) ret.add(nt) return list(ret) @@ -472,7 +500,8 @@ def sensors_temperatures(): high = None name = "Core %s" % cpu ret["coretemp"].append( - _common.shwtemp(name, current, high, high)) + _common.shwtemp(name, current, high, high) + ) except NotImplementedError: pass @@ -533,6 +562,7 @@ def pids(): if OPENBSD or NETBSD: + def pid_exists(pid): """Return True if pid exists.""" exists = _psposix.pid_exists(pid) @@ -542,6 +572,7 @@ def pid_exists(pid): return pid in pids() else: return True + else: pid_exists = _psposix.pid_exists @@ -558,6 +589,7 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: @@ -576,6 +608,7 @@ def wrapper(self, *args, **kwargs): else: raise raise + return wrapper @@ -706,7 +739,8 @@ def uids(self): return _common.puids( rawtuple[kinfo_proc_map['real_uid']], rawtuple[kinfo_proc_map['effective_uid']], - rawtuple[kinfo_proc_map['saved_uid']]) + rawtuple[kinfo_proc_map['saved_uid']], + ) @wrap_exceptions def gids(self): @@ -714,7 +748,8 @@ def gids(self): return _common.pgids( rawtuple[kinfo_proc_map['real_gid']], rawtuple[kinfo_proc_map['effective_gid']], - rawtuple[kinfo_proc_map['saved_gid']]) + rawtuple[kinfo_proc_map['saved_gid']], + ) @wrap_exceptions def cpu_times(self): @@ -723,9 +758,11 @@ def cpu_times(self): rawtuple[kinfo_proc_map['user_time']], rawtuple[kinfo_proc_map['sys_time']], rawtuple[kinfo_proc_map['ch_user_time']], - rawtuple[kinfo_proc_map['ch_sys_time']]) + rawtuple[kinfo_proc_map['ch_sys_time']], + ) if FREEBSD: + @wrap_exceptions def cpu_num(self): return self.oneshot()[kinfo_proc_map['cpunum']] @@ -738,7 +775,8 @@ def memory_info(self): rawtuple[kinfo_proc_map['vms']], rawtuple[kinfo_proc_map['memtext']], rawtuple[kinfo_proc_map['memdata']], - rawtuple[kinfo_proc_map['memstack']]) + rawtuple[kinfo_proc_map['memstack']], + ) memory_full_info = memory_info @@ -759,7 +797,8 @@ def num_ctx_switches(self): rawtuple = self.oneshot() return _common.pctxsw( rawtuple[kinfo_proc_map['ctx_switches_vol']], - rawtuple[kinfo_proc_map['ctx_switches_unvol']]) + rawtuple[kinfo_proc_map['ctx_switches_unvol']], + ) @wrap_exceptions def threads(self): @@ -776,8 +815,10 @@ def threads(self): @wrap_exceptions def connections(self, kind='inet'): if kind not in conn_tmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in conn_tmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap])) + ) families, types = conn_tmap[kind] ret = [] @@ -793,8 +834,9 @@ def connections(self, kind='inet'): if FREEBSD: if (fam not in families) or (type not in types): continue - nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, - TCP_STATUSES) + nt = conn_to_ntuple( + fd, fam, type, laddr, raddr, status, TCP_STATUSES + ) ret.append(nt) self._assert_alive() @@ -825,7 +867,8 @@ def io_counters(self): rawtuple[kinfo_proc_map['read_io_count']], rawtuple[kinfo_proc_map['write_io_count']], -1, - -1) + -1, + ) @wrap_exceptions def cwd(self): @@ -840,13 +883,15 @@ def cwd(self): return cext.proc_cwd(self.pid) else: raise NotImplementedError( - "supported only starting from FreeBSD 8" if - FREEBSD else "") + "supported only starting from FreeBSD 8" if FREEBSD else "" + ) nt_mmap_grouped = namedtuple( - 'mmap', 'path rss, private, ref_count, shadow_count') + 'mmap', 'path rss, private, ref_count, shadow_count' + ) nt_mmap_ext = namedtuple( - 'mmap', 'addr, perms path rss, private, ref_count, shadow_count') + 'mmap', 'addr, perms path rss, private, ref_count, shadow_count' + ) def _not_implemented(self): raise NotImplementedError @@ -854,17 +899,20 @@ def _not_implemented(self): # FreeBSD < 8 does not support functions based on kinfo_getfile() # and kinfo_getvmmap() if HAS_PROC_OPEN_FILES: + @wrap_exceptions def open_files(self): """Return files opened by process as a list of namedtuples.""" rawlist = cext.proc_open_files(self.pid) return [_common.popenfile(path, fd) for path, fd in rawlist] + else: open_files = _not_implemented # FreeBSD < 8 does not support functions based on kinfo_getfile() # and kinfo_getvmmap() if HAS_PROC_NUM_FDS: + @wrap_exceptions def num_fds(self): """Return the number of file descriptors opened by this process.""" @@ -872,6 +920,7 @@ def num_fds(self): if NETBSD: self._assert_alive() return ret + else: num_fds = _not_implemented @@ -891,8 +940,9 @@ def cpu_affinity_set(self, cpus): allcpus = tuple(range(len(per_cpu_times()))) for cpu in cpus: if cpu not in allcpus: - raise ValueError("invalid CPU #%i (choose between %s)" - % (cpu, allcpus)) + raise ValueError( + "invalid CPU #%i (choose between %s)" % (cpu, allcpus) + ) try: cext.proc_cpu_affinity_set(self.pid, cpus) except OSError as err: @@ -904,8 +954,9 @@ def cpu_affinity_set(self, cpus): for cpu in cpus: if cpu not in allcpus: raise ValueError( - "invalid CPU #%i (choose between %s)" % ( - cpu, allcpus)) + "invalid CPU #%i (choose between %s)" + % (cpu, allcpus) + ) raise @wrap_exceptions @@ -919,7 +970,8 @@ def rlimit(self, resource, limits=None): else: if len(limits) != 2: raise ValueError( - "second argument must be a (soft, hard) tuple, " - "got %s" % repr(limits)) + "second argument must be a (soft, hard) tuple, got %s" + % repr(limits) + ) soft, hard = limits return cext.proc_setrlimit(self.pid, resource, soft, hard) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index a252f3850..e19e37a97 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -58,6 +58,7 @@ enum = None +# fmt: off __extra__all__ = [ # 'PROCFS_PATH', @@ -69,6 +70,7 @@ "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ] +# fmt: on # ===================================================================== @@ -104,8 +106,9 @@ if enum is None: AF_LINK = socket.AF_PACKET else: - AddressFamily = enum.IntEnum('AddressFamily', - {'AF_LINK': int(socket.AF_PACKET)}) + AddressFamily = enum.IntEnum( + 'AddressFamily', {'AF_LINK': int(socket.AF_PACKET)} + ) AF_LINK = AddressFamily.AF_LINK # ioprio_* constants http://linux.die.net/man/2/ioprio_get @@ -115,6 +118,7 @@ IOPRIO_CLASS_BE = 2 IOPRIO_CLASS_IDLE = 3 else: + class IOPriority(enum.IntEnum): IOPRIO_CLASS_NONE = 0 IOPRIO_CLASS_RT = 1 @@ -163,6 +167,7 @@ class IOPriority(enum.IntEnum): # ===================================================================== +# fmt: off # psutil.virtual_memory() svmem = namedtuple( 'svmem', ['total', 'available', 'percent', 'used', 'free', @@ -197,6 +202,7 @@ class IOPriority(enum.IntEnum): pcputimes = namedtuple('pcputimes', ['user', 'system', 'children_user', 'children_system', 'iowait']) +# fmt: on # ===================================================================== @@ -311,8 +317,10 @@ def set_scputimes_ntuple(procfs_path): def prlimit(pid, resource_, limits=None): class StructRlimit(ctypes.Structure): - _fields_ = [('rlim_cur', ctypes.c_longlong), - ('rlim_max', ctypes.c_longlong)] + _fields_ = [ + ('rlim_cur', ctypes.c_longlong), + ('rlim_max', ctypes.c_longlong), + ] current = StructRlimit() if limits is None: @@ -324,7 +332,8 @@ class StructRlimit(ctypes.Structure): new.rlim_cur = limits[0] new.rlim_max = limits[1] ret = libc.prlimit( - pid, resource_, ctypes.byref(new), ctypes.byref(current)) + pid, resource_, ctypes.byref(new), ctypes.byref(current) + ) if ret != 0: errno_ = ctypes.get_errno() @@ -334,7 +343,8 @@ class StructRlimit(ctypes.Structure): if prlimit is not None: __extra__all__.extend( - [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()]) + [x for x in dir(cext) if x.startswith('RLIM') and x.isupper()] + ) # ===================================================================== @@ -379,8 +389,11 @@ def calculate_avail_vmem(mems): lru_inactive_file = mems[b'Inactive(file):'] slab_reclaimable = mems[b'SReclaimable:'] except KeyError as err: - debug("%r is missing from /proc/meminfo; using an approximation " - "for calculating available memory" % err.args[0]) + debug( + "%s is missing from /proc/meminfo; using an approximation for " + "calculating available memory" + % err.args[0] + ) return fallback try: f = open_binary('%s/zoneinfo' % get_procfs_path()) @@ -461,10 +474,11 @@ def virtual_memory(): inactive = mems[b"Inactive:"] except KeyError: try: - inactive = \ - mems[b"Inact_dirty:"] + \ - mems[b"Inact_clean:"] + \ - mems[b"Inact_laundry:"] + inactive = ( + mems[b"Inact_dirty:"] + + mems[b"Inact_clean:"] + + mems[b"Inact_laundry:"] + ) except KeyError: inactive = 0 missing_fields.append('inactive') @@ -516,11 +530,23 @@ def virtual_memory(): if missing_fields: msg = "%s memory stats couldn't be determined and %s set to 0" % ( ", ".join(missing_fields), - "was" if len(missing_fields) == 1 else "were") + "was" if len(missing_fields) == 1 else "were", + ) warnings.warn(msg, RuntimeWarning, stacklevel=2) - return svmem(total, avail, percent, used, free, - active, inactive, buffers, cached, shared, slab) + return svmem( + total, + avail, + percent, + used, + free, + active, + inactive, + buffers, + cached, + shared, + slab, + ) def swap_memory(): @@ -549,8 +575,10 @@ def swap_memory(): f = open_binary("%s/vmstat" % get_procfs_path()) except IOError as err: # see https://github.com/giampaolo/psutil/issues/722 - msg = "'sin' and 'sout' swap memory stats couldn't " + \ - "be determined and were set to 0 (%s)" % str(err) + msg = ( + "'sin' and 'sout' swap memory stats couldn't " + + "be determined and were set to 0 (%s)" % str(err) + ) warnings.warn(msg, RuntimeWarning, stacklevel=2) sin = sout = 0 else: @@ -592,7 +620,7 @@ def cpu_times(): set_scputimes_ntuple(procfs_path) with open_binary('%s/stat' % procfs_path) as f: values = f.readline().split() - fields = values[1:len(scputimes._fields) + 1] + fields = values[1 : len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] return scputimes(*fields) @@ -610,7 +638,7 @@ def per_cpu_times(): for line in f: if line.startswith(b'cpu'): values = line.split() - fields = values[1:len(scputimes._fields) + 1] + fields = values[1 : len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] entry = scputimes(*fields) cpus.append(entry) @@ -673,8 +701,9 @@ def cpu_count_cores(): if not line: # new section try: - mapping[current_info[b'physical id']] = \ - current_info[b'cpu cores'] + mapping[current_info[b'physical id']] = current_info[ + b'cpu cores' + ] except KeyError: pass current_info = {} @@ -701,12 +730,16 @@ def cpu_stats(): interrupts = int(line.split()[1]) elif line.startswith(b'softirq'): soft_interrupts = int(line.split()[1]) - if ctx_switches is not None and soft_interrupts is not None \ - and interrupts is not None: + if ( + ctx_switches is not None + and soft_interrupts is not None + and interrupts is not None + ): break syscalls = 0 return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls) + ctx_switches, interrupts, soft_interrupts, syscalls + ) def _cpu_get_cpuinfo_freq(): @@ -719,17 +752,19 @@ def _cpu_get_cpuinfo_freq(): return ret -if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \ - os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"): +if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or os.path.exists( + "/sys/devices/system/cpu/cpu0/cpufreq" +): + def cpu_freq(): """Return frequency metrics for all CPUs. Contrarily to other OSes, Linux updates these values in real-time. """ cpuinfo_freqs = _cpu_get_cpuinfo_freq() - paths = \ - glob.glob("/sys/devices/system/cpu/cpufreq/policy[0-9]*") or \ - glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq") + paths = glob.glob( + "/sys/devices/system/cpu/cpufreq/policy[0-9]*" + ) or glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq") paths.sort(key=lambda x: int(re.search(r"[0-9]+", x).group())) ret = [] pjoin = os.path.join @@ -754,11 +789,12 @@ def cpu_freq(): return ret else: + def cpu_freq(): """Alternate implementation using /proc/cpuinfo. min and max frequencies are not available and are set to None. """ - return [_common.scpufreq(x, 0., 0.) for x in _cpu_get_cpuinfo_freq()] + return [_common.scpufreq(x, 0.0, 0.0) for x in _cpu_get_cpuinfo_freq()] # ===================================================================== @@ -888,11 +924,13 @@ def decode_address(addr, family): if LITTLE_ENDIAN: ip = socket.inet_ntop( socket.AF_INET6, - struct.pack('>4I', *struct.unpack('<4I', ip))) + struct.pack('>4I', *struct.unpack('<4I', ip)), + ) else: ip = socket.inet_ntop( socket.AF_INET6, - struct.pack('<4I', *struct.unpack('<4I', ip))) + struct.pack('<4I', *struct.unpack('<4I', ip)), + ) except ValueError: # see: https://github.com/giampaolo/psutil/issues/623 if not supports_ipv6(): @@ -911,12 +949,14 @@ def process_inet(file, family, type_, inodes, filter_pid=None): f.readline() # skip the first line for lineno, line in enumerate(f, 1): try: - _, laddr, raddr, status, _, _, _, _, _, inode = \ + _, laddr, raddr, status, _, _, _, _, _, inode = ( line.split()[:10] + ) except ValueError: raise RuntimeError( - "error while parsing %s; malformed line %s %r" % ( - file, lineno, line)) + "error while parsing %s; malformed line %s %r" + % (file, lineno, line) + ) if inode in inodes: # # We assume inet sockets are unique, so we error # # out if there are multiple references to the @@ -955,8 +995,9 @@ def process_unix(file, family, inodes, filter_pid=None): # see: https://github.com/giampaolo/psutil/issues/766 continue raise RuntimeError( - "error while parsing %s; malformed line %r" % ( - file, line)) + "error while parsing %s; malformed line %r" + % (file, line) + ) if inode in inodes: # noqa # With UNIX sockets we can have a single inode # referencing many file descriptors. @@ -978,8 +1019,10 @@ def process_unix(file, family, inodes, filter_pid=None): def retrieve(self, kind, pid=None): if kind not in self.tmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in self.tmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in self.tmap])) + ) self._procfs_path = get_procfs_path() if pid is not None: inodes = self.get_proc_inodes(pid) @@ -993,17 +1036,19 @@ def retrieve(self, kind, pid=None): path = "%s/net/%s" % (self._procfs_path, proto_name) if family in (socket.AF_INET, socket.AF_INET6): ls = self.process_inet( - path, family, type_, inodes, filter_pid=pid) + path, family, type_, inodes, filter_pid=pid + ) else: - ls = self.process_unix( - path, family, inodes, filter_pid=pid) + ls = self.process_unix(path, family, inodes, filter_pid=pid) for fd, family, type_, laddr, raddr, status, bound_pid in ls: if pid: - conn = _common.pconn(fd, family, type_, laddr, raddr, - status) + conn = _common.pconn( + fd, family, type_, laddr, raddr, status + ) else: - conn = _common.sconn(fd, family, type_, laddr, raddr, - status, bound_pid) + conn = _common.sconn( + fd, family, type_, laddr, raddr, status, bound_pid + ) ret.add(conn) return list(ret) @@ -1027,37 +1072,49 @@ def net_io_counters(): colon = line.rfind(':') assert colon > 0, repr(line) name = line[:colon].strip() - fields = line[colon + 1:].strip().split() + fields = line[colon + 1 :].strip().split() # in - (bytes_recv, - packets_recv, - errin, - dropin, - fifoin, # unused - framein, # unused - compressedin, # unused - multicastin, # unused - # out - bytes_sent, - packets_sent, - errout, - dropout, - fifoout, # unused - collisionsout, # unused - carrierout, # unused - compressedout) = map(int, fields) - - retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv, - errin, errout, dropin, dropout) + ( + bytes_recv, + packets_recv, + errin, + dropin, + fifoin, # unused + framein, # unused + compressedin, # unused + multicastin, # unused + # out + bytes_sent, + packets_sent, + errout, + dropout, + fifoout, # unused + collisionsout, # unused + carrierout, # unused + compressedout, + ) = map(int, fields) + + retdict[name] = ( + bytes_sent, + bytes_recv, + packets_sent, + packets_recv, + errin, + errout, + dropin, + dropout, + ) return retdict def net_if_stats(): """Get NIC stats (isup, duplex, speed, mtu).""" - duplex_map = {cext.DUPLEX_FULL: NIC_DUPLEX_FULL, - cext.DUPLEX_HALF: NIC_DUPLEX_HALF, - cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN} + duplex_map = { + cext.DUPLEX_FULL: NIC_DUPLEX_FULL, + cext.DUPLEX_HALF: NIC_DUPLEX_HALF, + cext.DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN, + } names = net_io_counters().keys() ret = {} for name in names: @@ -1074,8 +1131,9 @@ def net_if_stats(): else: output_flags = ','.join(flags) isup = 'running' in flags - ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu, - output_flags) + ret[name] = _common.snicstats( + isup, duplex_map[duplex], speed, mtu, output_flags + ) return ret @@ -1091,6 +1149,7 @@ def disk_io_counters(perdisk=False): """Return disk I/O statistics for every disk installed on the system as a dict of raw tuples. """ + def read_procfs(): # OK, this is a bit confusing. The format of /proc/diskstats can # have 3 variations. @@ -1113,6 +1172,7 @@ def read_procfs(): for line in lines: fields = line.split() flen = len(fields) + # fmt: off if flen == 15: # Linux 2.4 name = fields[3] @@ -1133,6 +1193,7 @@ def read_procfs(): raise ValueError("not sure how to interpret line %r" % line) yield (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, writes_merged, busy_time) + # fmt: on def read_sysfs(): for block in os.listdir('/sys/block'): @@ -1142,10 +1203,12 @@ def read_sysfs(): with open_text(os.path.join(root, 'stat')) as f: fields = f.read().strip().split() name = os.path.basename(root) + # fmt: off (reads, reads_merged, rbytes, rtime, writes, writes_merged, wbytes, wtime, _, busy_time) = map(int, fields[:10]) yield (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, writes_merged, busy_time) + # fmt: on if os.path.exists('%s/diskstats' % get_procfs_path()): gen = read_procfs() @@ -1154,10 +1217,13 @@ def read_sysfs(): else: raise NotImplementedError( "%s/diskstats nor /sys/block filesystem are available on this " - "system" % get_procfs_path()) + "system" + % get_procfs_path() + ) retdict = {} for entry in gen: + # fmt: off (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, writes_merged, busy_time) = entry if not perdisk and not is_storage_device(name): @@ -1178,6 +1244,7 @@ def read_sysfs(): wbytes *= DISK_SECTOR_SIZE retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime, reads_merged, writes_merged, busy_time) + # fmt: on return retdict @@ -1291,8 +1358,9 @@ def disk_partitions(all=False): if device == '' or fstype not in fstypes: continue maxfile = maxpath = None # set later - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, - maxfile, maxpath) + ntuple = _common.sdiskpart( + device, mountpoint, fstype, opts, maxfile, maxpath + ) retlist.append(ntuple) return retlist @@ -1329,7 +1397,8 @@ def sensors_temperatures(): # https://github.com/giampaolo/psutil/issues/1708 # https://github.com/giampaolo/psutil/pull/1648 basenames2 = glob.glob( - '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*') + '/sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_*' + ) repl = re.compile('/sys/devices/platform/coretemp.*/hwmon/') for name in basenames2: altname = repl.sub('/sys/class/hwmon/', name) @@ -1386,19 +1455,23 @@ def sensors_temperatures(): continue trip_paths = glob.glob(base + '/trip_point*') - trip_points = set(['_'.join( - os.path.basename(p).split('_')[0:3]) for p in trip_paths]) + trip_points = set([ + '_'.join(os.path.basename(p).split('_')[0:3]) + for p in trip_paths + ]) critical = None high = None for trip_point in trip_points: path = os.path.join(base, trip_point + "_type") trip_type = cat(path, fallback='').strip() if trip_type == 'critical': - critical = bcat(os.path.join(base, trip_point + "_temp"), - fallback=None) + critical = bcat( + os.path.join(base, trip_point + "_temp"), fallback=None + ) elif trip_type == 'high': - high = bcat(os.path.join(base, trip_point + "_temp"), - fallback=None) + high = bcat( + os.path.join(base, trip_point + "_temp"), fallback=None + ) if high is not None: try: @@ -1469,8 +1542,11 @@ def multi_bcat(*paths): return ret.strip() return None - bats = [x for x in os.listdir(POWER_SUPPLY_PATH) if x.startswith('BAT') or - 'battery' in x.lower()] + bats = [ + x + for x in os.listdir(POWER_SUPPLY_PATH) + if x.startswith('BAT') or 'battery' in x.lower() + ] if not bats: return None # Get the first available battery. Usually this is "BAT0", except @@ -1479,15 +1555,9 @@ def multi_bcat(*paths): root = os.path.join(POWER_SUPPLY_PATH, sorted(bats)[0]) # Base metrics. - energy_now = multi_bcat( - root + "/energy_now", - root + "/charge_now") - power_now = multi_bcat( - root + "/power_now", - root + "/current_now") - energy_full = multi_bcat( - root + "/energy_full", - root + "/charge_full") + energy_now = multi_bcat(root + "/energy_now", root + "/charge_now") + power_now = multi_bcat(root + "/power_now", root + "/current_now") + energy_full = multi_bcat(root + "/energy_full", root + "/charge_full") time_to_empty = multi_bcat(root + "/time_to_empty_now") # Percent. If we have energy_full the percentage will be more @@ -1508,7 +1578,8 @@ def multi_bcat(*paths): power_plugged = None online = multi_bcat( os.path.join(POWER_SUPPLY_PATH, "AC0/online"), - os.path.join(POWER_SUPPLY_PATH, "AC/online")) + os.path.join(POWER_SUPPLY_PATH, "AC/online"), + ) if online is not None: power_plugged = online == 1 else: @@ -1565,8 +1636,7 @@ def boot_time(): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret - raise RuntimeError( - "line 'btime' not found in %s" % path) + raise RuntimeError("line 'btime' not found in %s" % path) # ===================================================================== @@ -1627,7 +1697,7 @@ def ppid_map(): pass else: rpar = data.rfind(b')') - dset = data[rpar + 2:].split() + dset = data[rpar + 2 :].split() ppid = int(dset[1]) ret[pid] = ppid return ret @@ -1637,6 +1707,7 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError and IOError exceptions into NoSuchProcess and AccessDenied. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: @@ -1651,6 +1722,7 @@ def wrapper(self, *args, **kwargs): if not os.path.exists("%s/%s" % (self._procfs_path, self.pid)): raise NoSuchProcess(self.pid, self._name) raise + return wrapper @@ -1678,7 +1750,7 @@ def _is_zombie(self): return False else: rpar = data.rfind(b')') - status = data[rpar + 2:rpar + 3] + status = data[rpar + 2 : rpar + 3] return status == b"Z" def _raise_if_zombie(self): @@ -1707,8 +1779,8 @@ def _parse_stat_file(self): # other parentheses. This is taken into account by looking for # the first occurrence of "(" and the last occurrence of ")". rpar = data.rfind(b')') - name = data[data.find(b'(') + 1:rpar] - fields = data[rpar + 2:].split() + name = data[data.find(b'(') + 1 : rpar] + fields = data[rpar + 2 :].split() ret = {} ret['name'] = name @@ -1815,6 +1887,7 @@ def terminal(self): # May not be available on old kernels. if os.path.exists('/proc/%s/io' % os.getpid()): + @wrap_exceptions def io_counters(self): fname = "%s/%s/io" % (self._procfs_path, self.pid) @@ -1843,8 +1916,10 @@ def io_counters(self): fields[b'wchar'], # write chars ) except KeyError as err: - raise ValueError("%r field was not found in %s; found fields " - "are %r" % (err.args[0], fname, fields)) + raise ValueError( + "%r field was not found in %s; found fields are %r" + % (err.args[0], fname, fields) + ) @wrap_exceptions def cpu_times(self): @@ -1890,8 +1965,9 @@ def memory_info(self): # | dirty | dirty pages (unused in Linux 2.6) | dt | | # ============================================================ with open_binary("%s/%s/statm" % (self._procfs_path, self.pid)) as f: - vms, rss, shared, text, lib, data, dirty = \ - (int(x) * PAGESIZE for x in f.readline().split()[:7]) + vms, rss, shared, text, lib, data, dirty = ( + int(x) * PAGESIZE for x in f.readline().split()[:7] + ) return pmem(rss, vms, shared, text, lib, data, dirty) if HAS_PROC_SMAPS_ROLLUP or HAS_PROC_SMAPS: @@ -1906,8 +1982,9 @@ def _parse_smaps_rollup(self): # fallback, which is slower but has a +50% success rate # compared to /proc/pid/smaps_rollup. uss = pss = swap = 0 - with open_binary("{}/{}/smaps_rollup".format( - self._procfs_path, self.pid)) as f: + with open_binary( + "{}/{}/smaps_rollup".format(self._procfs_path, self.pid) + ) as f: for line in f: if line.startswith(b"Private_"): # Private_Clean, Private_Dirty, Private_Hugetlb @@ -1920,11 +1997,12 @@ def _parse_smaps_rollup(self): @wrap_exceptions def _parse_smaps( - self, - # Gets Private_Clean, Private_Dirty, Private_Hugetlb. - _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"), - _pss_re=re.compile(br"\nPss\:\s+(\d+)"), - _swap_re=re.compile(br"\nSwap\:\s+(\d+)")): + self, + # Gets Private_Clean, Private_Dirty, Private_Hugetlb. + _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"), + _pss_re=re.compile(br"\nPss\:\s+(\d+)"), + _swap_re=re.compile(br"\nSwap\:\s+(\d+)"), + ): # /proc/pid/smaps does not exist on kernels < 2.6.14 or if # CONFIG_MMU kernel configuration option is not enabled. @@ -1954,8 +2032,10 @@ def memory_full_info(self): try: uss, pss, swap = self._parse_smaps_rollup() except (ProcessLookupError, FileNotFoundError) as err: - debug("ignore %r for pid %s and retry using " - "/proc/pid/smaps" % (err, self.pid)) + debug( + "ignore %r for pid %s and retry using /proc/pid/smaps" + % (err, self.pid) + ) uss, pss, swap = self._parse_smaps() else: uss, pss, swap = self._parse_smaps() @@ -1976,6 +2056,7 @@ def memory_maps(self): /proc/{PID}/smaps does not exist on kernels < 2.6.14 or if CONFIG_MMU kernel configuration option is not enabled. """ + def get_blocks(lines, current_block): data = {} for line in lines: @@ -1992,8 +2073,10 @@ def get_blocks(lines, current_block): # see issue #369 continue else: - raise ValueError("don't know how to inte" - "rpret line %r" % line) + raise ValueError( + "don't know how to interpret line %r" + % line + ) yield (current_block.pop(), data) data = self._read_smaps_file() @@ -2011,19 +2094,21 @@ def get_blocks(lines, current_block): try: addr, perms, offset, dev, inode, path = hfields except ValueError: - addr, perms, offset, dev, inode, path = \ - hfields + [''] + addr, perms, offset, dev, inode, path = hfields + [''] if not path: path = '[anon]' else: if PY3: path = decode(path) path = path.strip() - if (path.endswith(' (deleted)') and not - path_exists_strict(path)): + if path.endswith(' (deleted)') and not path_exists_strict( + path + ): path = path[:-10] ls.append(( - decode(addr), decode(perms), path, + decode(addr), + decode(perms), + path, data.get(b'Rss:', 0), data.get(b'Size:', 0), data.get(b'Pss:', 0), @@ -2042,16 +2127,17 @@ def cwd(self): return readlink("%s/%s/cwd" % (self._procfs_path, self.pid)) @wrap_exceptions - def num_ctx_switches(self, - _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)')): + def num_ctx_switches( + self, _ctxsw_re=re.compile(br'ctxt_switches:\t(\d+)') + ): data = self._read_status_file() ctxsw = _ctxsw_re.findall(data) if not ctxsw: raise NotImplementedError( "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'" "lines were not found in %s/%s/status; the kernel is " - "probably older than 2.6.23" % ( - self._procfs_path, self.pid)) + "probably older than 2.6.23" % (self._procfs_path, self.pid) + ) else: return _common.pctxsw(int(ctxsw[0]), int(ctxsw[1])) @@ -2071,7 +2157,10 @@ def threads(self): hit_enoent = False for thread_id in thread_ids: fname = "%s/%s/task/%s/stat" % ( - self._procfs_path, self.pid, thread_id) + self._procfs_path, + self.pid, + thread_id, + ) try: with open_binary(fname) as f: st = f.read().strip() @@ -2081,7 +2170,7 @@ def threads(self): hit_enoent = True continue # ignore the first two values ("pid (exe)") - st = st[st.find(b')') + 2:] + st = st[st.find(b')') + 2 :] values = st.split(b' ') utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS @@ -2112,7 +2201,8 @@ def cpu_affinity_get(self): return cext.proc_cpu_affinity_get(self.pid) def _get_eligible_cpus( - self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)")): + self, _re=re.compile(br"Cpus_allowed_list:\t(\d+)-(\d+)") + ): # See: https://github.com/giampaolo/psutil/issues/956 data = self._read_status_file() match = _re.findall(data) @@ -2132,12 +2222,14 @@ def cpu_affinity_set(self, cpus): for cpu in cpus: if cpu not in all_cpus: raise ValueError( - "invalid CPU number %r; choose between %s" % ( - cpu, eligible_cpus)) + "invalid CPU number %r; choose between %s" + % (cpu, eligible_cpus) + ) if cpu not in eligible_cpus: raise ValueError( "CPU number %r is not eligible; choose " - "between %s" % (cpu, eligible_cpus)) + "between %s" % (cpu, eligible_cpus) + ) raise # only starting from kernel 2.6.13 @@ -2178,9 +2270,11 @@ def rlimit(self, resource_, limits=None): else: # set if len(limits) != 2: - raise ValueError( - "second argument must be a (soft, hard) tuple, " - "got %s" % repr(limits)) + msg = ( + "second argument must be a (soft, hard) " + + "tuple, got %s" % repr(limits) + ) + raise ValueError(msg) prlimit(self.pid, resource_, limits) except OSError as err: if err.errno == errno.ENOSYS: @@ -2227,7 +2321,10 @@ def open_files(self): if path.startswith('/') and isfile_strict(path): # Get file position and flags. file = "%s/%s/fdinfo/%s" % ( - self._procfs_path, self.pid, fd) + self._procfs_path, + self.pid, + fd, + ) try: with open_binary(file) as f: pos = int(f.readline().split()[1]) @@ -2239,7 +2336,8 @@ def open_files(self): else: mode = file_flags_to_mode(flags) ntuple = popenfile( - path, int(fd), int(pos), mode, flags) + path, int(fd), int(pos), mode, flags + ) retlist.append(ntuple) if hit_enoent: self._raise_if_not_alive() diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 482a9d430..673ac0db7 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -91,6 +91,7 @@ # ===================================================================== +# fmt: off # psutil.cpu_times() scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle']) # psutil.virtual_memory() @@ -101,6 +102,7 @@ pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) # psutil.Process.memory_full_info() pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) +# fmt: on # ===================================================================== @@ -121,8 +123,7 @@ def virtual_memory(): # cmdline utility. free -= speculative percent = usage_percent((total - avail), total, round_=1) - return svmem(total, avail, percent, used, free, - active, inactive, wired) + return svmem(total, avail, percent, used, free, active, inactive, wired) def swap_memory(): @@ -164,10 +165,12 @@ def cpu_count_cores(): def cpu_stats(): - ctx_switches, interrupts, soft_interrupts, syscalls, traps = \ + ctx_switches, interrupts, soft_interrupts, syscalls, traps = ( cext.cpu_stats() + ) return _common.scpustats( - ctx_switches, interrupts, soft_interrupts, syscalls) + ctx_switches, interrupts, soft_interrupts, syscalls + ) def cpu_freq(): @@ -201,8 +204,9 @@ def disk_partitions(all=False): if not os.path.isabs(device) or not os.path.exists(device): continue maxfile = maxpath = None # set later - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, - maxfile, maxpath) + ntuple = _common.sdiskpart( + device, mountpoint, fstype, opts, maxfile, maxpath + ) retlist.append(ntuple) return retlist @@ -274,8 +278,9 @@ def net_if_stats(): duplex = _common.NicDuplex(duplex) output_flags = ','.join(flags) isup = 'running' in flags - ret[name] = _common.snicstats(isup, duplex, speed, mtu, - output_flags) + ret[name] = _common.snicstats( + isup, duplex, speed, mtu, output_flags + ) return ret @@ -340,6 +345,7 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: @@ -351,6 +357,7 @@ def wrapper(self, *args, **kwargs): raise NoSuchProcess(self.pid, self._name) except PermissionError: raise AccessDenied(self.pid, self._name) + return wrapper @@ -420,7 +427,8 @@ def uids(self): return _common.puids( rawtuple[kinfo_proc_map['ruid']], rawtuple[kinfo_proc_map['euid']], - rawtuple[kinfo_proc_map['suid']]) + rawtuple[kinfo_proc_map['suid']], + ) @wrap_exceptions def gids(self): @@ -428,7 +436,8 @@ def gids(self): return _common.puids( rawtuple[kinfo_proc_map['rgid']], rawtuple[kinfo_proc_map['egid']], - rawtuple[kinfo_proc_map['sgid']]) + rawtuple[kinfo_proc_map['sgid']], + ) @wrap_exceptions def terminal(self): @@ -453,7 +462,7 @@ def memory_info(self): def memory_full_info(self): basic_mem = self.memory_info() uss = cext.proc_memory_uss(self.pid) - return pfullmem(*basic_mem + (uss, )) + return pfullmem(*basic_mem + (uss,)) @wrap_exceptions def cpu_times(self): @@ -462,7 +471,9 @@ def cpu_times(self): rawtuple[pidtaskinfo_map['cpuutime']], rawtuple[pidtaskinfo_map['cpustime']], # children user / system times are not retrievable (set to 0) - 0.0, 0.0) + 0.0, + 0.0, + ) @wrap_exceptions def create_time(self): @@ -495,15 +506,18 @@ def open_files(self): @wrap_exceptions def connections(self, kind='inet'): if kind not in conn_tmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in conn_tmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap])) + ) families, types = conn_tmap[kind] rawlist = cext.proc_connections(self.pid, families, types) ret = [] for item in rawlist: fd, fam, type, laddr, raddr, status = item - nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, - TCP_STATUSES) + nt = conn_to_ntuple( + fd, fam, type, laddr, raddr, status, TCP_STATUSES + ) ret.append(nt) return ret diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 22d7c4f0d..42bdfa7ef 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -63,7 +63,8 @@ def pid_exists(pid): # https://bugs.python.org/issue21076 if enum is not None and hasattr(signal, "Signals"): Negsignal = enum.IntEnum( - 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals])) + 'Negsignal', dict([(x.name, -x.value) for x in signal.Signals]) + ) def negsig_to_enum(num): """Convert a negative signal value to an enum.""" @@ -71,17 +72,23 @@ def negsig_to_enum(num): return Negsignal(num) except ValueError: return num + else: # pragma: no cover + def negsig_to_enum(num): return num -def wait_pid(pid, timeout=None, proc_name=None, - _waitpid=os.waitpid, - _timer=getattr(time, 'monotonic', time.time), # noqa: B008 - _min=min, - _sleep=time.sleep, - _pid_exists=pid_exists): +def wait_pid( + pid, + timeout=None, + proc_name=None, + _waitpid=os.waitpid, + _timer=getattr(time, 'monotonic', time.time), # noqa: B008 + _min=min, + _sleep=time.sleep, + _pid_exists=pid_exists, +): """Wait for a process PID to terminate. If the process terminated normally by calling exit(3) or _exit(2), @@ -194,13 +201,13 @@ def disk_usage(path): # Total space which is only available to root (unless changed # at system level). - total = (st.f_blocks * st.f_frsize) + total = st.f_blocks * st.f_frsize # Remaining free space usable by root. - avail_to_root = (st.f_bfree * st.f_frsize) + avail_to_root = st.f_bfree * st.f_frsize # Remaining free space usable by user. - avail_to_user = (st.f_bavail * st.f_frsize) + avail_to_user = st.f_bavail * st.f_frsize # Total space being used in general. - used = (total - avail_to_root) + used = total - avail_to_root if MACOS: # see: https://github.com/giampaolo/psutil/pull/2152 used = _psutil_osx.disk_usage_used(path, used) @@ -216,7 +223,8 @@ def disk_usage(path): # reserved blocks that we are currently not considering: # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462 return sdiskusage( - total=total, used=used, free=avail_to_user, percent=usage_percent_user) + total=total, used=used, free=avail_to_user, percent=usage_percent_user + ) @memoize diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 4061af014..dddbece1f 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -89,7 +89,8 @@ uid=8, euid=9, gid=10, - egid=11) + egid=11, +) # ===================================================================== @@ -100,19 +101,22 @@ # psutil.cpu_times() scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) # psutil.cpu_times(percpu=True) -pcputimes = namedtuple('pcputimes', - ['user', 'system', 'children_user', 'children_system']) +pcputimes = namedtuple( + 'pcputimes', ['user', 'system', 'children_user', 'children_system'] +) # psutil.virtual_memory() svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) # psutil.Process.memory_info() pmem = namedtuple('pmem', ['rss', 'vms']) pfullmem = pmem # psutil.Process.memory_maps(grouped=True) -pmmap_grouped = namedtuple('pmmap_grouped', - ['path', 'rss', 'anonymous', 'locked']) +pmmap_grouped = namedtuple( + 'pmmap_grouped', ['path', 'rss', 'anonymous', 'locked'] +) # psutil.Process.memory_maps(grouped=False) pmmap_ext = namedtuple( - 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) + 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields) +) # ===================================================================== @@ -140,9 +144,15 @@ def swap_memory(): # usr/src/cmd/swap/swap.c # ...nevertheless I can't manage to obtain the same numbers as 'swap' # cmdline utility, so let's parse its output (sigh!) - p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' % - os.environ['PATH'], 'swap', '-l'], - stdout=subprocess.PIPE) + p = subprocess.Popen( + [ + '/usr/bin/env', + 'PATH=/usr/sbin:/sbin:%s' % os.environ['PATH'], + 'swap', + '-l', + ], + stdout=subprocess.PIPE, + ) stdout, _ = p.communicate() if PY3: stdout = stdout.decode(sys.stdout.encoding) @@ -161,8 +171,9 @@ def swap_memory(): free += int(int(f) * 512) used = total - free percent = usage_percent(used, total, round_=1) - return _common.sswap(total, used, free, percent, - sin * PAGE_SIZE, sout * PAGE_SIZE) + return _common.sswap( + total, used, free, percent, sin * PAGE_SIZE, sout * PAGE_SIZE + ) # ===================================================================== @@ -200,8 +211,9 @@ def cpu_stats(): """Return various CPU stats as a named tuple.""" ctx_switches, interrupts, syscalls, traps = cext.cpu_stats() soft_interrupts = 0 - return _common.scpustats(ctx_switches, interrupts, soft_interrupts, - syscalls) + return _common.scpustats( + ctx_switches, interrupts, soft_interrupts, syscalls + ) # ===================================================================== @@ -235,8 +247,9 @@ def disk_partitions(all=False): debug("skipping %r: %s" % (mountpoint, err)) continue maxfile = maxpath = None # set later - ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, - maxfile, maxpath) + ntuple = _common.sdiskpart( + device, mountpoint, fstype, opts, maxfile, maxpath + ) retlist.append(ntuple) return retlist @@ -259,8 +272,10 @@ def net_connections(kind, _pid=-1): if _pid == -1: cmap.pop('unix', 0) if kind not in cmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in cmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in cmap])) + ) families, types = _common.conn_tmap[kind] rawlist = cext.net_connections(_pid) ret = set() @@ -346,6 +361,7 @@ def wrap_exceptions(fun): """Call callable into a try/except clause and translate ENOENT, EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: @@ -367,6 +383,7 @@ def wrapper(self, *args, **kwargs): else: raise raise + return wrapper @@ -405,8 +422,9 @@ def _proc_name_and_args(self): @wrap_exceptions @memoize_when_activated def _proc_basic_info(self): - if self.pid == 0 and not \ - os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)): + if self.pid == 0 and not os.path.exists( + '%s/%s/psinfo' % (self._procfs_path, self.pid) + ): raise AccessDenied(self.pid) ret = cext.proc_basic_info(self.pid, self._procfs_path) assert len(ret) == len(proc_info_map) @@ -426,9 +444,10 @@ def name(self): def exe(self): try: return os.readlink( - "%s/%s/path/a.out" % (self._procfs_path, self.pid)) + "%s/%s/path/a.out" % (self._procfs_path, self.pid) + ) except OSError: - pass # continue and guess the exe name from the cmdline + pass # continue and guess the exe name from the cmdline # Will be guessed later from cmdline but we want to explicitly # invoke cmdline here in order to get an AccessDenied # exception if the user has not enough privileges. @@ -519,13 +538,13 @@ def cpu_num(self): def terminal(self): procfs_path = self._procfs_path hit_enoent = False - tty = wrap_exceptions( - self._proc_basic_info()[proc_info_map['ttynr']]) + tty = wrap_exceptions(self._proc_basic_info()[proc_info_map['ttynr']]) if tty != cext.PRNODEV: for x in (0, 1, 2, 255): try: return os.readlink( - '%s/%d/path/%d' % (procfs_path, self.pid, x)) + '%s/%d/path/%d' % (procfs_path, self.pid, x) + ) except FileNotFoundError: hit_enoent = True continue @@ -570,7 +589,8 @@ def threads(self): tid = int(tid) try: utime, stime = cext.query_process_thread( - self.pid, tid, procfs_path) + self.pid, tid, procfs_path + ) except EnvironmentError as err: if err.errno == errno.EOVERFLOW and not IS_64_BIT: # We may get here if we attempt to query a 64bit process @@ -619,12 +639,14 @@ def _get_unix_sockets(self, pid): # TODO: rewrite this in C (...but the damn netstat source code # does not include this part! Argh!!) cmd = ["pfiles", str(pid)] - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) stdout, stderr = p.communicate() if PY3: - stdout, stderr = (x.decode(sys.stdout.encoding) - for x in (stdout, stderr)) + stdout, stderr = ( + x.decode(sys.stdout.encoding) for x in (stdout, stderr) + ) if p.returncode != 0: if 'permission denied' in stderr.lower(): raise AccessDenied(self.pid, self._name) @@ -660,8 +682,10 @@ def connections(self, kind='inet'): # UNIX sockets if kind in ('all', 'unix'): - ret.extend([_common.pconn(*conn) for conn in - self._get_unix_sockets(self.pid)]) + ret.extend([ + _common.pconn(*conn) + for conn in self._get_unix_sockets(self.pid) + ]) return ret nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked') @@ -670,8 +694,10 @@ def connections(self, kind='inet'): @wrap_exceptions def memory_maps(self): def toaddr(start, end): - return '%s-%s' % (hex(start)[2:].strip('L'), - hex(end)[2:].strip('L')) + return '%s-%s' % ( + hex(start)[2:].strip('L'), + hex(end)[2:].strip('L'), + ) procfs_path = self._procfs_path retlist = [] @@ -696,7 +722,8 @@ def toaddr(start, end): if not name.startswith('['): try: name = os.readlink( - '%s/%s/path/%s' % (procfs_path, self.pid, name)) + '%s/%s/path/%s' % (procfs_path, self.pid, name) + ) except OSError as err: if err.errno == errno.ENOENT: # sometimes the link may not be resolved by @@ -721,7 +748,8 @@ def num_fds(self): @wrap_exceptions def num_ctx_switches(self): return _common.pctxsw( - *cext.proc_num_ctx_switches(self.pid, self._procfs_path)) + *cext.proc_num_ctx_switches(self.pid, self._procfs_path) + ) @wrap_exceptions def wait(self, timeout=None): diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index f44a7010c..2d3a0c9fd 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -43,8 +43,10 @@ try: from . import _psutil_windows as cext except ImportError as err: - if str(err).lower().startswith("dll load failed") and \ - sys.getwindowsversion()[0] < 6: + if ( + str(err).lower().startswith("dll load failed") + and sys.getwindowsversion()[0] < 6 + ): # We may get here if: # 1) we are on an old Windows version # 2) psutil was installed via pip + wheel @@ -63,6 +65,7 @@ # process priority constants, import from __init__.py: # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx +# fmt: off __extra__all__ = [ "win_service_iter", "win_service_get", # Process priority @@ -74,6 +77,7 @@ # others "CONN_DELETE_TCB", "AF_LINK", ] +# fmt: on # ===================================================================== @@ -107,6 +111,7 @@ } if enum is not None: + class Priority(enum.IntEnum): ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS @@ -123,11 +128,13 @@ class Priority(enum.IntEnum): IOPRIO_NORMAL = 2 IOPRIO_HIGH = 3 else: + class IOPriority(enum.IntEnum): IOPRIO_VERYLOW = 0 IOPRIO_LOW = 1 IOPRIO_NORMAL = 2 IOPRIO_HIGH = 3 + globals().update(IOPriority.__members__) pinfo_map = dict( @@ -161,6 +168,7 @@ class IOPriority(enum.IntEnum): # ===================================================================== +# fmt: off # psutil.cpu_times() scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'interrupt', 'dpc']) @@ -183,6 +191,7 @@ class IOPriority(enum.IntEnum): pio = namedtuple('pio', ['read_count', 'write_count', 'read_bytes', 'write_bytes', 'other_count', 'other_bytes']) +# fmt: on # ===================================================================== @@ -199,7 +208,7 @@ def convert_dos_path(s): """ rawdrive = '\\'.join(s.split('\\')[:3]) driveletter = cext.QueryDosDevice(rawdrive) - remainder = s[len(rawdrive):] + remainder = s[len(rawdrive) :] return os.path.join(driveletter, remainder) @@ -253,7 +262,7 @@ def swap_memory(): # while the corresponding free physical value is not decremented until # pages are accessed, so we can't use free system memory for swap. # instead, we calculate page file usage based on performance counter - if (total > 0): + if total > 0: percentswap = cext.swap_percent() used = int(0.01 * percentswap * total) else: @@ -303,8 +312,9 @@ def cpu_times(): # interrupt and dpc times. cext.per_cpu_times() does, so we # rely on it to get those only. percpu_summed = scputimes(*[sum(n) for n in zip(*cext.per_cpu_times())]) - return scputimes(user, system, idle, - percpu_summed.interrupt, percpu_summed.dpc) + return scputimes( + user, system, idle, percpu_summed.interrupt, percpu_summed.dpc + ) def per_cpu_times(): @@ -330,8 +340,9 @@ def cpu_stats(): """Return CPU statistics.""" ctx_switches, interrupts, dpcs, syscalls = cext.cpu_stats() soft_interrupts = 0 - return _common.scpustats(ctx_switches, interrupts, soft_interrupts, - syscalls) + return _common.scpustats( + ctx_switches, interrupts, soft_interrupts, syscalls + ) def cpu_freq(): @@ -371,15 +382,25 @@ def net_connections(kind, _pid=-1): connections (as opposed to connections opened by one process only). """ if kind not in conn_tmap: - raise ValueError("invalid %r kind argument; choose between %s" - % (kind, ', '.join([repr(x) for x in conn_tmap]))) + raise ValueError( + "invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap])) + ) families, types = conn_tmap[kind] rawlist = cext.net_connections(_pid, families, types) ret = set() for item in rawlist: fd, fam, type, laddr, raddr, status, pid = item - nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES, - pid=pid if _pid == -1 else None) + nt = conn_to_ntuple( + fd, + fam, + type, + laddr, + raddr, + status, + TCP_STATUSES, + pid=pid if _pid == -1 else None, + ) ret.add(nt) return list(ret) @@ -503,7 +524,9 @@ def __init__(self, name, display_name): def __str__(self): details = "(name=%r, display_name=%r)" % ( - self._name, self._display_name) + self._name, + self._display_name, + ) return "%s%s" % (self.__class__.__name__, details) def __repr__(self): @@ -521,14 +544,16 @@ def __ne__(self, other): def _query_config(self): with self._wrap_exceptions(): - display_name, binpath, username, start_type = \ + display_name, binpath, username, start_type = ( cext.winservice_query_config(self._name) + ) # XXX - update _self.display_name? return dict( display_name=py2_strencode(display_name), binpath=py2_strencode(binpath), username=py2_strencode(username), - start_type=py2_strencode(start_type)) + start_type=py2_strencode(start_type), + ) def _query_status(self): with self._wrap_exceptions(): @@ -546,15 +571,17 @@ def _wrap_exceptions(self): yield except OSError as err: if is_permission_err(err): - raise AccessDenied( - pid=None, name=self._name, - msg="service %r is not querable (not enough privileges)" % - self._name) - elif err.winerror in (cext.ERROR_INVALID_NAME, - cext.ERROR_SERVICE_DOES_NOT_EXIST): - raise NoSuchProcess( - pid=None, name=self._name, - msg="service %r does not exist)" % self._name) + msg = ( + "service %r is not querable (not enough privileges)" + % self._name + ) + raise AccessDenied(pid=None, name=self._name, msg=msg) + elif err.winerror in ( + cext.ERROR_INVALID_NAME, + cext.ERROR_SERVICE_DOES_NOT_EXIST, + ): + msg = "service %r does not exist" % self._name + raise NoSuchProcess(pid=None, name=self._name, msg=msg) else: raise @@ -671,12 +698,17 @@ def as_dict(self): def is_permission_err(exc): """Return True if this is a permission error.""" assert isinstance(exc, OSError), exc + if exc.errno in (errno.EPERM, errno.EACCES): + return True # On Python 2 OSError doesn't always have 'winerror'. Sometimes # it does, in which case the original exception was WindowsError # (which is a subclass of OSError). - return exc.errno in (errno.EPERM, errno.EACCES) or \ - getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED, - cext.ERROR_PRIVILEGE_NOT_HELD) + if getattr(exc, "winerror", -1) in ( + cext.ERROR_ACCESS_DENIED, + cext.ERROR_PRIVILEGE_NOT_HELD, + ): + return True + return False def convert_oserror(exc, pid=None, name=None): @@ -691,12 +723,14 @@ def convert_oserror(exc, pid=None, name=None): def wrap_exceptions(fun): """Decorator which converts OSError into NoSuchProcess or AccessDenied.""" + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) except OSError as err: raise convert_oserror(err, pid=self.pid, name=self._name) + return wrapper @@ -704,6 +738,7 @@ def retry_error_partial_copy(fun): """Workaround for https://github.com/giampaolo/psutil/issues/875. See: https://stackoverflow.com/questions/4457745#4457745. """ + @functools.wraps(fun) def wrapper(self, *args, **kwargs): delay = 0.0001 @@ -723,6 +758,7 @@ def wrapper(self, *args, **kwargs): "returning {}".format(fun, times, err) ) raise AccessDenied(pid=self.pid, name=self._name, msg=msg) + return wrapper @@ -859,7 +895,7 @@ def memory_full_info(self): basic_mem = self.memory_info() uss = cext.proc_memory_uss(self.pid) uss *= getpagesize() - return pfullmem(*basic_mem + (uss, )) + return pfullmem(*basic_mem + (uss,)) def memory_maps(self): try: @@ -885,8 +921,10 @@ def send_signal(self, sig): if sig == signal.SIGTERM: cext.proc_kill(self.pid) # py >= 2.7 - elif sig in (getattr(signal, "CTRL_C_EVENT", object()), - getattr(signal, "CTRL_BREAK_EVENT", object())): + elif sig in ( + getattr(signal, "CTRL_C_EVENT", object()), + getattr(signal, "CTRL_BREAK_EVENT", object()), + ): os.kill(self.pid, sig) else: msg = ( @@ -1047,8 +1085,12 @@ def ionice_set(self, ioclass, value): if value: msg = "value argument not accepted on Windows" raise TypeError(msg) - if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, - IOPRIO_HIGH): + if ioclass not in ( + IOPRIO_VERYLOW, + IOPRIO_LOW, + IOPRIO_NORMAL, + IOPRIO_HIGH, + ): raise ValueError("%s is not a valid priority" % ioclass) cext.proc_io_priority_set(self.pid, ioclass) @@ -1082,6 +1124,7 @@ def status(self): def cpu_affinity_get(self): def from_bitmask(x): return [i for i in range(64) if (1 << i) & x] + bitmask = cext.proc_cpu_affinity_get(self.pid) return from_bitmask(bitmask) @@ -1092,7 +1135,7 @@ def to_bitmask(ls): raise ValueError("invalid argument %r" % ls) out = 0 for b in ls: - out |= 2 ** b + out |= 2**b return out # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER @@ -1103,7 +1146,8 @@ def to_bitmask(ls): if cpu not in allcpus: if not isinstance(cpu, (int, long)): raise TypeError( - "invalid CPU %r; an integer is required" % cpu) + "invalid CPU %r; an integer is required" % cpu + ) else: raise ValueError("invalid CPU %r" % cpu) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 0dd92b8b9..05f0d63b8 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -76,6 +76,7 @@ from psutil._psposix import wait_pid +# fmt: off __all__ = [ # constants 'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'TOLERANCE_SYS_MEM', 'NO_RETRIES', @@ -112,6 +113,7 @@ # others 'warn', 'copyload_shared_lib', 'is_namedtuple', ] +# fmt: on # =================================================================== @@ -127,7 +129,7 @@ CI_TESTING = APPVEYOR or GITHUB_ACTIONS COVERAGE = 'COVERAGE_RUN' in os.environ # are we a 64 bit process? -IS_64BIT = sys.maxsize > 2 ** 32 +IS_64BIT = sys.maxsize > 2**32 @memoize @@ -194,10 +196,10 @@ def macos_version(): # --- paths ROOT_DIR = os.path.realpath( - os.path.join(os.path.dirname(__file__), '..', '..')) + os.path.join(os.path.dirname(__file__), '..', '..') +) SCRIPTS_DIR = os.environ.get( - "PSUTIL_SCRIPTS_DIR", - os.path.join(ROOT_DIR, 'scripts'), + "PSUTIL_SCRIPTS_DIR", os.path.join(ROOT_DIR, 'scripts') ) HERE = os.path.realpath(os.path.dirname(__file__)) @@ -231,7 +233,8 @@ def _get_py_exe(): def attempt(exe): try: subprocess.check_call( - [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) except subprocess.CalledProcessError: return None else: @@ -253,11 +256,12 @@ def attempt(exe): elif GITHUB_ACTIONS: return sys.executable, env elif MACOS: - exe = \ - attempt(sys.executable) or \ - attempt(os.path.realpath(sys.executable)) or \ - attempt(which("python%s.%s" % sys.version_info[:2])) or \ - attempt(psutil.Process().exe()) + exe = ( + attempt(sys.executable) + or attempt(os.path.realpath(sys.executable)) + or attempt(which("python%s.%s" % sys.version_info[:2])) + or attempt(psutil.Process().exe()) + ) if not exe: raise ValueError("can't find python exe real abspath") return exe, env @@ -271,8 +275,9 @@ def attempt(exe): DEVNULL = open(os.devnull, 'r+') atexit.register(DEVNULL.close) -VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil) - if x.startswith('STATUS_')] +VALID_PROC_STATUSES = [ + getattr(psutil, x) for x in dir(psutil) if x.startswith('STATUS_') +] AF_UNIX = getattr(socket, "AF_UNIX", object()) _subprocesses_started = set() @@ -340,6 +345,7 @@ def wrapper(*args, **kwargs): except Exception: reap_children() raise + return wrapper @@ -368,9 +374,9 @@ def spawn_testproc(cmd=None, **kwds): try: safe_rmpath(testfn) pyline = ( - "from time import sleep;" + - "open(r'%s', 'w').close();" % testfn + - "sleep(60);" + "from time import sleep;" + + "open(r'%s', 'w').close();" % testfn + + "sleep(60);" ) cmd = [PYTHON_EXE, "-c", pyline] sproc = subprocess.Popen(cmd, **kwds) @@ -527,6 +533,7 @@ def terminate(proc_or_pid, sig=signal.SIGTERM, wait_timeout=GLOBAL_TIMEOUT): Does nothing if the process does not exist. Return process exit status. """ + def wait(proc, timeout): if isinstance(proc, subprocess.Popen) and not PY3: proc.wait() @@ -683,13 +690,14 @@ def get_winver(): class retry: """A retry decorator.""" - def __init__(self, - exception=Exception, - timeout=None, - retries=None, - interval=0.001, - logfun=None, - ): + def __init__( + self, + exception=Exception, + timeout=None, + retries=None, + interval=0.001, + logfun=None, + ): if timeout and retries: raise ValueError("timeout and retries args are mutually exclusive") self.exception = exception @@ -738,8 +746,12 @@ def wrapper(*args, **kwargs): return wrapper -@retry(exception=psutil.NoSuchProcess, logfun=None, timeout=GLOBAL_TIMEOUT, - interval=0.001) +@retry( + exception=psutil.NoSuchProcess, + logfun=None, + timeout=GLOBAL_TIMEOUT, + interval=0.001, +) def wait_for_pid(pid): """Wait for pid to show up in the process list then return. Used in the test suite to give time the sub process to initialize. @@ -750,8 +762,12 @@ def wait_for_pid(pid): time.sleep(0.01) -@retry(exception=(FileNotFoundError, AssertionError), logfun=None, - timeout=GLOBAL_TIMEOUT, interval=0.001) +@retry( + exception=(FileNotFoundError, AssertionError), + logfun=None, + timeout=GLOBAL_TIMEOUT, + interval=0.001, +) def wait_for_file(fname, delete=True, empty=False): """Wait for a file to be written on disk with some content.""" with open(fname, "rb") as f: @@ -763,8 +779,12 @@ def wait_for_file(fname, delete=True, empty=False): return data -@retry(exception=AssertionError, logfun=None, timeout=GLOBAL_TIMEOUT, - interval=0.001) +@retry( + exception=AssertionError, + logfun=None, + timeout=GLOBAL_TIMEOUT, + interval=0.001, +) def call_until(fun, expr): """Keep calling function for timeout secs and exit if eval() expression is True. @@ -781,6 +801,7 @@ def call_until(fun, expr): def safe_rmpath(path): """Convenience function for removing temporary test files or dirs.""" + def retry_fun(fun): # On Windows it could happen that the file or directory has # open handles or references preventing the delete operation @@ -837,9 +858,8 @@ def create_exe(outpath, c_code=None): if c_code: if not which("gcc"): raise unittest.SkipTest("gcc is not installed") - if isinstance(c_code, bool): # c_code is True - c_code = textwrap.dedent( - """ + if isinstance(c_code, bool): # c_code is True + c_code = textwrap.dedent(""" #include int main() { pause(); @@ -887,7 +907,10 @@ def __str__(self): if not fqmod.startswith('psutil.'): fqmod = 'psutil.tests.' + fqmod return "%s.%s.%s" % ( - fqmod, self.__class__.__name__, self._testMethodName) + fqmod, + self.__class__.__name__, + self._testMethodName, + ) # assertRaisesRegexp renamed to assertRaisesRegex in 3.3; # add support for the new name. @@ -896,6 +919,7 @@ def __str__(self): # ...otherwise multiprocessing.Pool complains if not PY3: + def runTest(self): pass @@ -960,8 +984,7 @@ def assertPidGone(self, pid): try: psutil.Process(pid) except psutil.ZombieProcess: - raise AssertionError( - "wasn't supposed to raise ZombieProcess") + raise AssertionError("wasn't supposed to raise ZombieProcess") self.assertEqual(cm.exception.pid, pid) self.assertEqual(cm.exception.name, None) assert not psutil.pid_exists(pid), pid @@ -981,7 +1004,9 @@ def assertProcessGone(self, proc): self._check_proc_exc(proc, exc) else: msg = "Process.%s() didn't raise NSP and returned %r" % ( - name, ret) + name, + ret, + ) raise AssertionError(msg) proc.wait(timeout=0) # assert not raise TimeoutExpired @@ -1126,8 +1151,10 @@ def _check_fds(self, fun): after = self._get_num_fds() diff = after - before if diff < 0: - raise self.fail("negative diff %r (gc probably collected a " - "resource from a previous test)" % diff) + raise self.fail( + "negative diff %r (gc probably collected a " + "resource from a previous test)" % diff + ) if diff > 0: type_ = "fd" if POSIX else "handle" if diff > 1: @@ -1157,7 +1184,11 @@ def _check_mem(self, fun, times, retries, tolerance): for idx in range(1, retries + 1): mem = self._call_ntimes(fun, times) msg = "Run #%s: extra-mem=%s, per-call=%s, calls=%s" % ( - idx, bytes2human(mem), bytes2human(mem / times), times) + idx, + bytes2human(mem), + bytes2human(mem / times), + times, + ) messages.append(msg) success = mem <= tolerance or mem <= prev_mem if success: @@ -1177,12 +1208,14 @@ def _check_mem(self, fun, times, retries, tolerance): def call(self, fun): return fun() - def execute(self, fun, times=None, warmup_times=None, retries=None, - tolerance=None): + def execute( + self, fun, times=None, warmup_times=None, retries=None, tolerance=None + ): """Test a callable.""" times = times if times is not None else self.times - warmup_times = warmup_times if warmup_times is not None \ - else self.warmup_times + warmup_times = ( + warmup_times if warmup_times is not None else self.warmup_times + ) retries = retries if retries is not None else self.retries tolerance = tolerance if tolerance is not None else self.tolerance try: @@ -1201,6 +1234,7 @@ def execute_w_exc(self, exc, fun, **kwargs): """Convenience method to test a callable while making sure it raises an exception on every call. """ + def call(): self.assertRaises(exc, fun) @@ -1213,6 +1247,7 @@ def print_sysinfo(): import getpass import locale import pprint + try: import pip except ImportError: @@ -1230,14 +1265,14 @@ def print_sysinfo(): elif psutil.OSX: info['OS'] = 'Darwin %s' % platform.mac_ver()[0] elif psutil.WINDOWS: - info['OS'] = "Windows " + ' '.join( - map(str, platform.win32_ver())) + info['OS'] = "Windows " + ' '.join(map(str, platform.win32_ver())) if hasattr(platform, 'win32_edition'): info['OS'] += ", " + platform.win32_edition() else: info['OS'] = "%s %s" % (platform.system(), platform.version()) info['arch'] = ', '.join( - list(platform.architecture()) + [platform.machine()]) + list(platform.architecture()) + [platform.machine()] + ) if psutil.POSIX: info['kernel'] = platform.uname()[2] @@ -1245,7 +1280,8 @@ def print_sysinfo(): info['python'] = ', '.join([ platform.python_implementation(), platform.python_version(), - platform.python_compiler()]) + platform.python_compiler(), + ]) info['pip'] = getattr(pip, '__version__', 'not installed') if wheel is not None: info['pip'] += " (wheel=%s)" % wheel.__version__ @@ -1266,7 +1302,8 @@ def print_sysinfo(): lang = locale.getlocale() info['lang'] = '%s, %s' % (lang[0], lang[1]) info['boot-time'] = datetime.datetime.fromtimestamp( - psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S") + psutil.boot_time() + ).strftime("%Y-%m-%d %H:%M:%S") info['time'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") info['user'] = getpass.getuser() info['home'] = os.path.expanduser("~") @@ -1278,13 +1315,20 @@ def print_sysinfo(): # metrics info['cpus'] = psutil.cpu_count() info['loadavg'] = "%.1f%%, %.1f%%, %.1f%%" % ( - tuple([x / psutil.cpu_count() * 100 for x in psutil.getloadavg()])) + tuple([x / psutil.cpu_count() * 100 for x in psutil.getloadavg()]) + ) mem = psutil.virtual_memory() info['memory'] = "%s%%, used=%s, total=%s" % ( - int(mem.percent), bytes2human(mem.used), bytes2human(mem.total)) + int(mem.percent), + bytes2human(mem.used), + bytes2human(mem.total), + ) swap = psutil.swap_memory() info['swap'] = "%s%%, used=%s, total=%s" % ( - int(swap.percent), bytes2human(swap.used), bytes2human(swap.total)) + int(swap.percent), + bytes2human(swap.used), + bytes2human(swap.total), + ) info['pids'] = len(psutil.pids()) pinfo = psutil.Process().as_dict() pinfo.pop('memory_maps', None) @@ -1341,10 +1385,7 @@ class process_namespace: ... fun() """ - utils = [ - ('cpu_percent', (), {}), - ('memory_percent', (), {}), - ] + utils = [('cpu_percent', (), {}), ('memory_percent', (), {})] ignored = [ ('as_dict', (), {}), @@ -1355,7 +1396,7 @@ class process_namespace: ('parent', (), {}), ('parents', (), {}), ('pid', (), {}), - ('wait', (0, ), {}), + ('wait', (0,), {}), ] getters = [ @@ -1387,7 +1428,7 @@ class process_namespace: if HAS_IONICE: getters += [('ionice', (), {})] if HAS_RLIMIT: - getters += [('rlimit', (psutil.RLIMIT_NOFILE, ), {})] + getters += [('rlimit', (psutil.RLIMIT_NOFILE,), {})] if HAS_CPU_AFFINITY: getters += [('cpu_affinity', (), {})] if HAS_PROC_CPU_NUM: @@ -1401,29 +1442,29 @@ class process_namespace: setters = [] if POSIX: - setters += [('nice', (0, ), {})] + setters += [('nice', (0,), {})] else: - setters += [('nice', (psutil.NORMAL_PRIORITY_CLASS, ), {})] + setters += [('nice', (psutil.NORMAL_PRIORITY_CLASS,), {})] if HAS_RLIMIT: setters += [('rlimit', (psutil.RLIMIT_NOFILE, (1024, 4096)), {})] if HAS_IONICE: if LINUX: setters += [('ionice', (psutil.IOPRIO_CLASS_NONE, 0), {})] else: - setters += [('ionice', (psutil.IOPRIO_NORMAL, ), {})] + setters += [('ionice', (psutil.IOPRIO_NORMAL,), {})] if HAS_CPU_AFFINITY: - setters += [('cpu_affinity', ([_get_eligible_cpu()], ), {})] + setters += [('cpu_affinity', ([_get_eligible_cpu()],), {})] killers = [ - ('send_signal', (signal.SIGTERM, ), {}), + ('send_signal', (signal.SIGTERM,), {}), ('suspend', (), {}), ('resume', (), {}), ('terminate', (), {}), ('kill', (), {}), ] if WINDOWS: - killers += [('send_signal', (signal.CTRL_C_EVENT, ), {})] - killers += [('send_signal', (signal.CTRL_BREAK_EVENT, ), {})] + killers += [('send_signal', (signal.CTRL_C_EVENT,), {})] + killers += [('send_signal', (signal.CTRL_BREAK_EVENT,), {})] all = utils + getters + setters + killers @@ -1456,7 +1497,9 @@ def test_class_coverage(cls, test_class, ls): meth_name = 'test_' + fun_name if not hasattr(test_class, meth_name): msg = "%r class should define a '%s' method" % ( - test_class.__class__.__name__, meth_name) + test_class.__class__.__name__, + meth_name, + ) raise AttributeError(msg) @classmethod @@ -1487,12 +1530,12 @@ class system_namespace: ('cpu_times', (), {'percpu': True}), ('disk_io_counters', (), {'perdisk': True}), ('disk_partitions', (), {'all': True}), - ('disk_usage', (os.getcwd(), ), {}), + ('disk_usage', (os.getcwd(),), {}), ('net_connections', (), {'kind': 'all'}), ('net_if_addrs', (), {}), ('net_if_stats', (), {}), ('net_io_counters', (), {'pernic': True}), - ('pid_exists', (os.getpid(), ), {}), + ('pid_exists', (os.getpid(),), {}), ('pids', (), {}), ('swap_memory', (), {}), ('users', (), {}), @@ -1510,11 +1553,11 @@ class system_namespace: getters += [('sensors_battery', (), {})] if WINDOWS: getters += [('win_service_iter', (), {})] - getters += [('win_service_get', ('alg', ), {})] + getters += [('win_service_get', ('alg',), {})] ignored = [ ('process_iter', (), {}), - ('wait_procs', ([psutil.Process()], ), {}), + ('wait_procs', ([psutil.Process()],), {}), ('cpu_percent', (), {}), ('cpu_times_percent', (), {}), ] @@ -1550,15 +1593,18 @@ def retry_on_failure(retries=NO_RETRIES): """Decorator which runs a test function and retries N times before actually failing. """ + def logfun(exc): print("%r, retrying" % exc, file=sys.stderr) # NOQA - return retry(exception=AssertionError, timeout=None, retries=retries, - logfun=logfun) + return retry( + exception=AssertionError, timeout=None, retries=retries, logfun=logfun + ) def skip_on_access_denied(only_if=None): """Decorator to Ignore AccessDenied exceptions.""" + def decorator(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): @@ -1569,12 +1615,15 @@ def wrapper(*args, **kwargs): if not only_if: raise raise unittest.SkipTest("raises AccessDenied") + return wrapper + return decorator def skip_on_not_implemented(only_if=None): """Decorator to Ignore NotImplementedError exceptions.""" + def decorator(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): @@ -1584,10 +1633,14 @@ def wrapper(*args, **kwargs): if only_if is not None: if not only_if: raise - msg = "%r was skipped because it raised NotImplementedError" \ - % fun.__name__ + msg = ( + "%r was skipped because it raised NotImplementedError" + % fun.__name__ + ) raise unittest.SkipTest(msg) + return wrapper + return decorator @@ -1714,6 +1767,7 @@ def check_net_address(addr, family): IPv6 and MAC addresses. """ import ipaddress # python >= 3.3 / requires "pip install ipaddress" + if enum and PY3 and not PYPY: assert isinstance(family, enum.IntEnum), family if family == socket.AF_INET: @@ -1737,6 +1791,7 @@ def check_net_address(addr, family): def check_connection_ntuple(conn): """Check validity of a connection namedtuple.""" + def check_ntuple(conn): has_pid = len(conn) == 7 assert len(conn) in (6, 7), len(conn) @@ -1773,8 +1828,11 @@ def check_family(conn): def check_type(conn): # SOCK_SEQPACKET may happen in case of AF_UNIX socks SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) - assert conn.type in (socket.SOCK_STREAM, socket.SOCK_DGRAM, - SOCK_SEQPACKET), conn.type + assert conn.type in ( + socket.SOCK_STREAM, + socket.SOCK_DGRAM, + SOCK_SEQPACKET, + ), conn.type if enum is not None: assert isinstance(conn.type, enum.IntEnum), conn else: @@ -1797,8 +1855,9 @@ def check_addrs(conn): def check_status(conn): assert isinstance(conn.status, str), conn.status - valids = [getattr(psutil, x) for x in dir(psutil) - if x.startswith('CONN_')] + valids = [ + getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_') + ] assert conn.status in valids, conn.status if conn.family in (AF_INET, AF_INET6) and conn.type == SOCK_STREAM: assert conn.status != psutil.CONN_NONE, conn.status @@ -1821,10 +1880,12 @@ def reload_module(module): """Backport of importlib.reload of Python 3.3+.""" try: import importlib + if not hasattr(importlib, 'reload'): # python <=3.3 raise ImportError except ImportError: import imp + return imp.reload(module) else: return importlib.reload(module) @@ -1834,9 +1895,11 @@ def import_module_by_path(path): name = os.path.splitext(os.path.basename(path))[0] if sys.version_info[0] < 3: import imp + return imp.load_source(name, path) else: import importlib.util + spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) @@ -1866,6 +1929,7 @@ def is_namedtuple(x): if POSIX: + @contextlib.contextmanager def copyload_shared_lib(suffix=""): """Ctx manager which picks up a random shared CO lib used @@ -1875,9 +1939,11 @@ def copyload_shared_lib(suffix=""): exe = 'pypy' if PYPY else 'python' ext = ".so" dst = get_testfn(suffix=suffix + ext) - libs = [x.path for x in psutil.Process().memory_maps() if - os.path.splitext(x.path)[1] == ext and - exe in x.path.lower()] + libs = [ + x.path + for x in psutil.Process().memory_maps() + if os.path.splitext(x.path)[1] == ext and exe in x.path.lower() + ] src = random.choice(libs) shutil.copyfile(src, dst) try: @@ -1885,7 +1951,9 @@ def copyload_shared_lib(suffix=""): yield dst finally: safe_rmpath(dst) + else: + @contextlib.contextmanager def copyload_shared_lib(suffix=""): """Ctx manager which picks up a random shared DLL lib used @@ -1895,15 +1963,22 @@ def copyload_shared_lib(suffix=""): """ from ctypes import WinError from ctypes import wintypes + ext = ".dll" dst = get_testfn(suffix=suffix + ext) - libs = [x.path for x in psutil.Process().memory_maps() if - x.path.lower().endswith(ext) and - 'python' in os.path.basename(x.path).lower() and - 'wow64' not in x.path.lower()] + libs = [ + x.path + for x in psutil.Process().memory_maps() + if x.path.lower().endswith(ext) + and 'python' in os.path.basename(x.path).lower() + and 'wow64' not in x.path.lower() + ] if PYPY and not libs: - libs = [x.path for x in psutil.Process().memory_maps() if - 'pypy' in os.path.basename(x.path).lower()] + libs = [ + x.path + for x in psutil.Process().memory_maps() + if 'pypy' in os.path.basename(x.path).lower() + ] src = random.choice(libs) shutil.copyfile(src, dst) cfile = None diff --git a/psutil/tests/__main__.py b/psutil/tests/__main__.py index 0cf39cc80..2460abdb3 100644 --- a/psutil/tests/__main__.py +++ b/psutil/tests/__main__.py @@ -1,4 +1,3 @@ - # 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. diff --git a/psutil/tests/runner.py b/psutil/tests/runner.py index a678b4c41..a054e4817 100755 --- a/psutil/tests/runner.py +++ b/psutil/tests/runner.py @@ -58,7 +58,9 @@ USE_COLORS = not CI_TESTING and term_supports_colors() HERE = os.path.abspath(os.path.dirname(__file__)) -loadTestsFromTestCase = unittest.defaultTestLoader.loadTestsFromTestCase # noqa +loadTestsFromTestCase = ( # noqa: N816 + unittest.defaultTestLoader.loadTestsFromTestCase +) def cprint(msg, color, bold=False, file=None): @@ -78,10 +80,13 @@ class TestLoader: skip_files.extend(['test_osx.py', 'test_linux.py', 'test_posix.py']) def _get_testmods(self): - return [os.path.join(self.testdir, x) - for x in os.listdir(self.testdir) - if x.startswith('test_') and x.endswith('.py') and - x not in self.skip_files] + return [ + os.path.join(self.testdir, x) + for x in os.listdir(self.testdir) + if x.startswith('test_') + and x.endswith('.py') + and x not in self.skip_files + ] def _iter_testmod_classes(self): """Iterate over all test files in this directory and return @@ -91,8 +96,9 @@ def _iter_testmod_classes(self): mod = import_module_by_path(path) for name in dir(mod): obj = getattr(mod, name) - if isinstance(obj, type) and \ - issubclass(obj, unittest.TestCase): + if isinstance(obj, type) and issubclass( + obj, unittest.TestCase + ): yield obj def all(self): @@ -121,7 +127,6 @@ def from_name(self, name): class ColouredResult(unittest.TextTestResult): - def addSuccess(self, test): unittest.TestResult.addSuccess(self, test) cprint("OK", "green") @@ -200,7 +205,6 @@ def run(self, suite): class ParallelRunner(ColouredTextRunner): - @staticmethod def _parallelize(suite): def fdopen(fd, mode, *kwds): @@ -240,8 +244,11 @@ def run(self, suite): par_suite = self._parallelize(par_suite) # run parallel - cprint("starting parallel tests using %s workers" % NWORKERS, - "green", bold=True) + cprint( + "starting parallel tests using %s workers" % NWORKERS, + "green", + bold=True, + ) t = time.time() par = self._run(par_suite) par_elapsed = time.time() - t @@ -262,13 +269,15 @@ def run(self, suite): # print if not par.wasSuccessful() and ser_suite.countTestCases() > 0: par.printErrors() # print them again at the bottom - par_fails, par_errs, par_skips = map(len, (par.failures, - par.errors, - par.skipped)) - ser_fails, ser_errs, ser_skips = map(len, (ser.failures, - ser.errors, - ser.skipped)) - print(textwrap.dedent(""" + par_fails, par_errs, par_skips = map( + len, (par.failures, par.errors, par.skipped) + ) + ser_fails, ser_errs, ser_skips = map( + len, (ser.failures, ser.errors, ser.skipped) + ) + print( + textwrap.dedent( + """ +----------+----------+----------+----------+----------+----------+ | | total | failures | errors | skipped | time | +----------+----------+----------+----------+----------+----------+ @@ -276,10 +285,29 @@ def run(self, suite): +----------+----------+----------+----------+----------+----------+ | serial | %3s | %3s | %3s | %3s | %.2fs | +----------+----------+----------+----------+----------+----------+ - """ % (par.testsRun, par_fails, par_errs, par_skips, par_elapsed, - ser.testsRun, ser_fails, ser_errs, ser_skips, ser_elapsed))) - print("Ran %s tests in %.3fs using %s workers" % ( - par.testsRun + ser.testsRun, par_elapsed + ser_elapsed, NWORKERS)) + """ + % ( + par.testsRun, + par_fails, + par_errs, + par_skips, + par_elapsed, + ser.testsRun, + ser_fails, + ser_errs, + ser_skips, + ser_elapsed, + ) + ) + ) + print( + "Ran %s tests in %.3fs using %s workers" + % ( + par.testsRun + ser.testsRun, + par_elapsed + ser_elapsed, + NWORKERS, + ) + ) ok = par.wasSuccessful() and ser.wasSuccessful() self._exit(ok) @@ -287,6 +315,7 @@ def run(self, suite): def get_runner(parallel=False): def warn(msg): cprint(msg + " Running serial tests instead.", "red") + if parallel: if psutil.WINDOWS: warn("Can't run parallel tests on Windows.") @@ -316,12 +345,18 @@ def main(): setup() usage = "python3 -m psutil.tests [opts] [test-name]" parser = optparse.OptionParser(usage=usage, description="run unit tests") - parser.add_option("--last-failed", - action="store_true", default=False, - help="only run last failed tests") - parser.add_option("--parallel", - action="store_true", default=False, - help="run tests in parallel") + parser.add_option( + "--last-failed", + action="store_true", + default=False, + help="only run last failed tests", + ) + parser.add_option( + "--parallel", + action="store_true", + default=False, + help="run tests in parallel", + ) opts, args = parser.parse_args() if not opts.last_failed: diff --git a/psutil/tests/test_aix.py b/psutil/tests/test_aix.py index 4a23b774a..e7e0c8aa5 100755 --- a/psutil/tests/test_aix.py +++ b/psutil/tests/test_aix.py @@ -19,7 +19,6 @@ @unittest.skipIf(not AIX, "AIX only") class AIXSpecificTestCase(PsutilTestCase): - def test_virtual_memory(self): out = sh('/usr/bin/svmon -O unit=KB') re_pattern = r"memory\s*" @@ -28,7 +27,8 @@ def test_virtual_memory(self): matchobj = re.search(re_pattern, out) self.assertIsNotNone( - matchobj, "svmon command returned unexpected output") + matchobj, "svmon command returned unexpected output" + ) KB = 1024 total = int(matchobj.group("size")) * KB @@ -41,14 +41,17 @@ def test_virtual_memory(self): # TOLERANCE_SYS_MEM from psutil.tests is not enough. For some reason # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance # when compared to GBs. - TOLERANCE_SYS_MEM = 2 * KB * KB # 2 MB + TOLERANCE_SYS_MEM = 2 * KB * KB # 2 MB self.assertEqual(psutil_result.total, total) self.assertAlmostEqual( - psutil_result.used, used, delta=TOLERANCE_SYS_MEM) + psutil_result.used, used, delta=TOLERANCE_SYS_MEM + ) self.assertAlmostEqual( - psutil_result.available, available, delta=TOLERANCE_SYS_MEM) + psutil_result.available, available, delta=TOLERANCE_SYS_MEM + ) self.assertAlmostEqual( - psutil_result.free, free, delta=TOLERANCE_SYS_MEM) + psutil_result.free, free, delta=TOLERANCE_SYS_MEM + ) def test_swap_memory(self): out = sh('/usr/sbin/lsps -a') @@ -56,16 +59,20 @@ def test_swap_memory(self): # we'll always have 'MB' in the result # TODO maybe try to use "swap -l" to check "used" too, but its units # are not guaranteed to be "MB" so parsing may not be consistent - matchobj = re.search(r"(?P\S+)\s+" - r"(?P\S+)\s+" - r"(?P\S+)\s+" - r"(?P\d+)MB", out) + matchobj = re.search( + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\d+)MB", + out, + ) self.assertIsNotNone( - matchobj, "lsps command returned unexpected output") + matchobj, "lsps command returned unexpected output" + ) total_mb = int(matchobj.group("size")) - MB = 1024 ** 2 + MB = 1024**2 psutil_result = psutil.swap_memory() # we divide our result by MB instead of multiplying the lsps value by # MB because lsps may round down, so we round down too @@ -75,14 +82,17 @@ def test_cpu_stats(self): out = sh('/usr/bin/mpstat -a') re_pattern = r"ALL\s*" - for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq " - "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd " - "sysc").split(): + for field in ( + "min maj mpcs mpcr dev soft dec ph cs ics bound rq " + "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd " + "sysc" + ).split(): re_pattern += r"(?P<%s>\S+)\s+" % (field,) matchobj = re.search(re_pattern, out) self.assertIsNotNone( - matchobj, "mpstat command returned unexpected output") + matchobj, "mpstat command returned unexpected output" + ) # numbers are usually in the millions so 1000 is ok for tolerance CPU_STATS_TOLERANCE = 1000 @@ -90,19 +100,23 @@ def test_cpu_stats(self): self.assertAlmostEqual( psutil_result.ctx_switches, int(matchobj.group("cs")), - delta=CPU_STATS_TOLERANCE) + delta=CPU_STATS_TOLERANCE, + ) self.assertAlmostEqual( psutil_result.syscalls, int(matchobj.group("sysc")), - delta=CPU_STATS_TOLERANCE) + delta=CPU_STATS_TOLERANCE, + ) self.assertAlmostEqual( psutil_result.interrupts, int(matchobj.group("dev")), - delta=CPU_STATS_TOLERANCE) + delta=CPU_STATS_TOLERANCE, + ) self.assertAlmostEqual( psutil_result.soft_interrupts, int(matchobj.group("soft")), - delta=CPU_STATS_TOLERANCE) + delta=CPU_STATS_TOLERANCE, + ) def test_cpu_count_logical(self): out = sh('/usr/bin/mpstat -a') @@ -119,4 +133,5 @@ def test_net_if_addrs_names(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_bsd.py b/psutil/tests/test_bsd.py index c5a5e7abc..7b502bcb8 100755 --- a/psutil/tests/test_bsd.py +++ b/psutil/tests/test_bsd.py @@ -48,9 +48,9 @@ def sysctl(cmdline): """ result = sh("sysctl " + cmdline) if FREEBSD: - result = result[result.find(": ") + 2:] + result = result[result.find(": ") + 2 :] elif OPENBSD or NETBSD: - result = result[result.find("=") + 1:] + result = result[result.find("=") + 1 :] try: return int(result) except ValueError: @@ -90,8 +90,9 @@ def test_process_create_time(self): output = sh("ps -o lstart -p %s" % self.pid) start_ps = output.replace('STARTED', '').strip() start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) + start_psutil = time.strftime( + "%a %b %e %H:%M:%S %Y", time.localtime(start_psutil) + ) self.assertEqual(start_ps, start_psutil) def test_disks(self): @@ -142,8 +143,9 @@ def test_net_if_stats(self): else: self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) if "mtu" in out: - self.assertEqual(stats.mtu, - int(re.findall(r'mtu (\d+)', out)[0])) + self.assertEqual( + stats.mtu, int(re.findall(r'mtu (\d+)', out)[0]) + ) # ===================================================================== @@ -153,7 +155,6 @@ def test_net_if_stats(self): @unittest.skipIf(not FREEBSD, "FREEBSD only") class FreeBSDPsutilTestCase(PsutilTestCase): - @classmethod def setUpClass(cls): cls.pid = spawn_testproc().pid @@ -179,13 +180,16 @@ def test_memory_maps(self): def test_exe(self): out = sh('procstat -b %s' % self.pid) - self.assertEqual(psutil.Process(self.pid).exe(), - out.split('\n')[1].split()[-1]) + self.assertEqual( + psutil.Process(self.pid).exe(), out.split('\n')[1].split()[-1] + ) def test_cmdline(self): out = sh('procstat -c %s' % self.pid) - self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), - ' '.join(out.split('\n')[1].split()[2:])) + self.assertEqual( + ' '.join(psutil.Process(self.pid).cmdline()), + ' '.join(out.split('\n')[1].split()[2:]), + ) def test_uids_gids(self): out = sh('procstat -s %s' % self.pid) @@ -243,7 +247,6 @@ def test_cpu_times(self): @unittest.skipIf(not FREEBSD, "FREEBSD only") class FreeBSDSystemTestCase(PsutilTestCase): - @staticmethod def parse_swapinfo(): # the last line is always the total @@ -282,38 +285,44 @@ def test_cpu_frequency_against_sysctl(self): @retry_on_failure() def test_vmem_active(self): syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().active, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().active, syst, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_vmem_inactive(self): syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().inactive, syst, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_vmem_wired(self): syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().wired, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().wired, syst, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_vmem_cached(self): syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().cached, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().cached, syst, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_vmem_free(self): syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().free, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().free, syst, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_vmem_buffers(self): syst = sysctl("vfs.bufspace") - self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().buffers, syst, delta=TOLERANCE_SYS_MEM + ) # --- virtual_memory(); tests against muse @@ -326,61 +335,79 @@ def test_muse_vmem_total(self): @retry_on_failure() def test_muse_vmem_active(self): num = muse('Active') - self.assertAlmostEqual(psutil.virtual_memory().active, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().active, num, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") @retry_on_failure() def test_muse_vmem_inactive(self): num = muse('Inactive') - self.assertAlmostEqual(psutil.virtual_memory().inactive, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().inactive, num, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") @retry_on_failure() def test_muse_vmem_wired(self): num = muse('Wired') - self.assertAlmostEqual(psutil.virtual_memory().wired, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().wired, num, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") @retry_on_failure() def test_muse_vmem_cached(self): num = muse('Cache') - self.assertAlmostEqual(psutil.virtual_memory().cached, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().cached, num, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") @retry_on_failure() def test_muse_vmem_free(self): num = muse('Free') - self.assertAlmostEqual(psutil.virtual_memory().free, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().free, num, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not MUSE_AVAILABLE, "muse not installed") @retry_on_failure() def test_muse_vmem_buffers(self): num = muse('Buffer') - self.assertAlmostEqual(psutil.virtual_memory().buffers, num, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + psutil.virtual_memory().buffers, num, delta=TOLERANCE_SYS_MEM + ) def test_cpu_stats_ctx_switches(self): - self.assertAlmostEqual(psutil.cpu_stats().ctx_switches, - sysctl('vm.stats.sys.v_swtch'), delta=1000) + self.assertAlmostEqual( + psutil.cpu_stats().ctx_switches, + sysctl('vm.stats.sys.v_swtch'), + delta=1000, + ) def test_cpu_stats_interrupts(self): - self.assertAlmostEqual(psutil.cpu_stats().interrupts, - sysctl('vm.stats.sys.v_intr'), delta=1000) + self.assertAlmostEqual( + psutil.cpu_stats().interrupts, + sysctl('vm.stats.sys.v_intr'), + delta=1000, + ) def test_cpu_stats_soft_interrupts(self): - self.assertAlmostEqual(psutil.cpu_stats().soft_interrupts, - sysctl('vm.stats.sys.v_soft'), delta=1000) + self.assertAlmostEqual( + psutil.cpu_stats().soft_interrupts, + sysctl('vm.stats.sys.v_soft'), + delta=1000, + ) @retry_on_failure() def test_cpu_stats_syscalls(self): # pretty high tolerance but it looks like it's OK. - self.assertAlmostEqual(psutil.cpu_stats().syscalls, - sysctl('vm.stats.sys.v_syscall'), delta=200000) + self.assertAlmostEqual( + psutil.cpu_stats().syscalls, + sysctl('vm.stats.sys.v_syscall'), + delta=200000, + ) # def test_cpu_stats_traps(self): # self.assertAlmostEqual(psutil.cpu_stats().traps, @@ -391,24 +418,27 @@ def test_cpu_stats_syscalls(self): def test_swapmem_free(self): total, used, free = self.parse_swapinfo() self.assertAlmostEqual( - psutil.swap_memory().free, free, delta=TOLERANCE_SYS_MEM) + psutil.swap_memory().free, free, delta=TOLERANCE_SYS_MEM + ) def test_swapmem_used(self): total, used, free = self.parse_swapinfo() self.assertAlmostEqual( - psutil.swap_memory().used, used, delta=TOLERANCE_SYS_MEM) + psutil.swap_memory().used, used, delta=TOLERANCE_SYS_MEM + ) def test_swapmem_total(self): total, used, free = self.parse_swapinfo() self.assertAlmostEqual( - psutil.swap_memory().total, total, delta=TOLERANCE_SYS_MEM) + psutil.swap_memory().total, total, delta=TOLERANCE_SYS_MEM + ) # --- others def test_boot_time(self): s = sysctl('sysctl kern.boottime') - s = s[s.find(" sec = ") + 7:] - s = s[:s.find(',')] + s = s[s.find(" sec = ") + 7 :] + s = s[: s.find(',')] btime = int(s) self.assertEqual(btime, psutil.boot_time()) @@ -422,8 +452,9 @@ def secs2hours(secs): return "%d:%02d" % (h, m) out = sh("acpiconf -i 0") - fields = dict([(x.split('\t')[0], x.split('\t')[-1]) - for x in out.split("\n")]) + fields = dict( + [(x.split('\t')[0], x.split('\t')[-1]) for x in out.split("\n")] + ) metrics = psutil.sensors_battery() percent = int(fields['Remaining capacity:'].replace('%', '')) remaining_time = fields['Remaining time:'] @@ -435,10 +466,13 @@ def secs2hours(secs): @unittest.skipIf(not HAS_BATTERY, "no battery") def test_sensors_battery_against_sysctl(self): - self.assertEqual(psutil.sensors_battery().percent, - sysctl("hw.acpi.battery.life")) - self.assertEqual(psutil.sensors_battery().power_plugged, - sysctl("hw.acpi.acline") == 1) + self.assertEqual( + psutil.sensors_battery().percent, sysctl("hw.acpi.battery.life") + ) + self.assertEqual( + psutil.sensors_battery().power_plugged, + sysctl("hw.acpi.acline") == 1, + ) secsleft = psutil.sensors_battery().secsleft if secsleft < 0: self.assertEqual(sysctl("hw.acpi.battery.time"), -1) @@ -469,13 +503,16 @@ def test_sensors_temperatures_against_sysctl(self): self.skipTest("temperatures not supported by kernel") self.assertAlmostEqual( psutil.sensors_temperatures()["coretemp"][cpu].current, - sysctl_result, delta=10) + sysctl_result, + delta=10, + ) sensor = "dev.cpu.%s.coretemp.tjmax" % cpu sysctl_result = int(float(sysctl(sensor)[:-1])) self.assertEqual( psutil.sensors_temperatures()["coretemp"][cpu].high, - sysctl_result) + sysctl_result, + ) # ===================================================================== @@ -485,7 +522,6 @@ def test_sensors_temperatures_against_sysctl(self): @unittest.skipIf(not OPENBSD, "OPENBSD only") class OpenBSDTestCase(PsutilTestCase): - def test_boot_time(self): s = sysctl('kern.boottime') sys_bt = datetime.datetime.strptime(s, "%a %b %d %H:%M:%S %Y") @@ -500,7 +536,6 @@ def test_boot_time(self): @unittest.skipIf(not NETBSD, "NETBSD only") class NetBSDTestCase(PsutilTestCase): - @staticmethod def parse_meminfo(look_for): with open('/proc/meminfo') as f: @@ -513,39 +548,52 @@ def parse_meminfo(look_for): def test_vmem_total(self): self.assertEqual( - psutil.virtual_memory().total, self.parse_meminfo("MemTotal:")) + psutil.virtual_memory().total, self.parse_meminfo("MemTotal:") + ) def test_vmem_free(self): self.assertAlmostEqual( - psutil.virtual_memory().free, self.parse_meminfo("MemFree:"), - delta=TOLERANCE_SYS_MEM) + psutil.virtual_memory().free, + self.parse_meminfo("MemFree:"), + delta=TOLERANCE_SYS_MEM, + ) def test_vmem_buffers(self): self.assertAlmostEqual( - psutil.virtual_memory().buffers, self.parse_meminfo("Buffers:"), - delta=TOLERANCE_SYS_MEM) + psutil.virtual_memory().buffers, + self.parse_meminfo("Buffers:"), + delta=TOLERANCE_SYS_MEM, + ) def test_vmem_shared(self): self.assertAlmostEqual( - psutil.virtual_memory().shared, self.parse_meminfo("MemShared:"), - delta=TOLERANCE_SYS_MEM) + psutil.virtual_memory().shared, + self.parse_meminfo("MemShared:"), + delta=TOLERANCE_SYS_MEM, + ) def test_vmem_cached(self): self.assertAlmostEqual( - psutil.virtual_memory().cached, self.parse_meminfo("Cached:"), - delta=TOLERANCE_SYS_MEM) + psutil.virtual_memory().cached, + self.parse_meminfo("Cached:"), + delta=TOLERANCE_SYS_MEM, + ) # --- swap mem def test_swapmem_total(self): self.assertAlmostEqual( - psutil.swap_memory().total, self.parse_meminfo("SwapTotal:"), - delta=TOLERANCE_SYS_MEM) + psutil.swap_memory().total, + self.parse_meminfo("SwapTotal:"), + delta=TOLERANCE_SYS_MEM, + ) def test_swapmem_free(self): self.assertAlmostEqual( - psutil.swap_memory().free, self.parse_meminfo("SwapFree:"), - delta=TOLERANCE_SYS_MEM) + psutil.swap_memory().free, + self.parse_meminfo("SwapFree:"), + delta=TOLERANCE_SYS_MEM, + ) def test_swapmem_used(self): smem = psutil.swap_memory() @@ -562,7 +610,8 @@ def test_cpu_stats_interrupts(self): else: raise ValueError("couldn't find line") self.assertAlmostEqual( - psutil.cpu_stats().interrupts, interrupts, delta=1000) + psutil.cpu_stats().interrupts, interrupts, delta=1000 + ) def test_cpu_stats_ctx_switches(self): with open('/proc/stat', 'rb') as f: @@ -573,9 +622,11 @@ def test_cpu_stats_ctx_switches(self): else: raise ValueError("couldn't find line") self.assertAlmostEqual( - psutil.cpu_stats().ctx_switches, ctx_switches, delta=1000) + psutil.cpu_stats().ctx_switches, ctx_switches, delta=1000 + ) if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index 70929b84c..5924dbb2c 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -50,7 +50,6 @@ @serialrun class ConnectionTestCase(PsutilTestCase): - def setUp(self): if NETBSD or FREEBSD or (MACOS and not PY3): # Process opens a UNIX socket to /var/log/run. @@ -88,7 +87,6 @@ def compare_procsys_connections(self, pid, proc_cons, kind='all'): class TestBasicOperations(ConnectionTestCase): - @unittest.skipIf(SKIP_SYSCONS, "requires root") def test_system(self): with create_sockets(): @@ -136,7 +134,8 @@ def check_socket(self, sock): self.assertEqual(conn.family, sock.family) # see: http://bugs.python.org/issue30204 self.assertEqual( - conn.type, sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)) + conn.type, sock.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) + ) # local address laddr = sock.getsockname() @@ -257,7 +256,6 @@ def test_unix(self): class TestFilters(ConnectionTestCase): - def test_filters(self): def check(kind, families, types): for conn in thisproc.connections(kind=kind): @@ -269,45 +267,43 @@ def check(kind, families, types): self.assertIn(conn.type, types) with create_sockets(): - check('all', - [AF_INET, AF_INET6, AF_UNIX], - [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) - check('inet', - [AF_INET, AF_INET6], - [SOCK_STREAM, SOCK_DGRAM]) - check('inet4', - [AF_INET], - [SOCK_STREAM, SOCK_DGRAM]) - check('tcp', - [AF_INET, AF_INET6], - [SOCK_STREAM]) - check('tcp4', - [AF_INET], - [SOCK_STREAM]) - check('tcp6', - [AF_INET6], - [SOCK_STREAM]) - check('udp', - [AF_INET, AF_INET6], - [SOCK_DGRAM]) - check('udp4', - [AF_INET], - [SOCK_DGRAM]) - check('udp6', - [AF_INET6], - [SOCK_DGRAM]) + check( + 'all', + [AF_INET, AF_INET6, AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET], + ) + check('inet', [AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]) + check('inet4', [AF_INET], [SOCK_STREAM, SOCK_DGRAM]) + check('tcp', [AF_INET, AF_INET6], [SOCK_STREAM]) + check('tcp4', [AF_INET], [SOCK_STREAM]) + check('tcp6', [AF_INET6], [SOCK_STREAM]) + check('udp', [AF_INET, AF_INET6], [SOCK_DGRAM]) + check('udp4', [AF_INET], [SOCK_DGRAM]) + check('udp6', [AF_INET6], [SOCK_DGRAM]) if HAS_CONNECTIONS_UNIX: - check('unix', - [AF_UNIX], - [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET]) + check( + 'unix', + [AF_UNIX], + [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET], + ) @skip_on_access_denied(only_if=MACOS) def test_combos(self): reap_children() def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): - all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4", - "tcp6", "udp", "udp4", "udp6") + all_kinds = ( + "all", + "inet", + "inet4", + "inet6", + "tcp", + "tcp4", + "tcp6", + "udp", + "udp4", + "udp6", + ) check_connection_ntuple(conn) self.assertEqual(conn.family, family) self.assertEqual(conn.type, type) @@ -348,13 +344,17 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): # must be relative on Windows testfile = os.path.basename(self.get_testfn(dir=os.getcwd())) tcp4_template = tcp_template.format( - family=int(AF_INET), addr="127.0.0.1", testfn=testfile) + family=int(AF_INET), addr="127.0.0.1", testfn=testfile + ) udp4_template = udp_template.format( - family=int(AF_INET), addr="127.0.0.1", testfn=testfile) + family=int(AF_INET), addr="127.0.0.1", testfn=testfile + ) tcp6_template = tcp_template.format( - family=int(AF_INET6), addr="::1", testfn=testfile) + family=int(AF_INET6), addr="::1", testfn=testfile + ) udp6_template = udp_template.format( - family=int(AF_INET6), addr="::1", testfn=testfile) + family=int(AF_INET6), addr="::1", testfn=testfile + ) # launch various subprocess instantiating a socket of various # families and types to enrich psutil results @@ -379,24 +379,52 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): for conn in cons: # TCP v4 if p.pid == tcp4_proc.pid: - check_conn(p, conn, AF_INET, SOCK_STREAM, tcp4_addr, (), - psutil.CONN_LISTEN, - ("all", "inet", "inet4", "tcp", "tcp4")) + check_conn( + p, + conn, + AF_INET, + SOCK_STREAM, + tcp4_addr, + (), + psutil.CONN_LISTEN, + ("all", "inet", "inet4", "tcp", "tcp4"), + ) # UDP v4 elif p.pid == udp4_proc.pid: - check_conn(p, conn, AF_INET, SOCK_DGRAM, udp4_addr, (), - psutil.CONN_NONE, - ("all", "inet", "inet4", "udp", "udp4")) + check_conn( + p, + conn, + AF_INET, + SOCK_DGRAM, + udp4_addr, + (), + psutil.CONN_NONE, + ("all", "inet", "inet4", "udp", "udp4"), + ) # TCP v6 elif p.pid == getattr(tcp6_proc, "pid", None): - check_conn(p, conn, AF_INET6, SOCK_STREAM, tcp6_addr, (), - psutil.CONN_LISTEN, - ("all", "inet", "inet6", "tcp", "tcp6")) + check_conn( + p, + conn, + AF_INET6, + SOCK_STREAM, + tcp6_addr, + (), + psutil.CONN_LISTEN, + ("all", "inet", "inet6", "tcp", "tcp6"), + ) # UDP v6 elif p.pid == getattr(udp6_proc, "pid", None): - check_conn(p, conn, AF_INET6, SOCK_DGRAM, udp6_addr, (), - psutil.CONN_NONE, - ("all", "inet", "inet6", "udp", "udp6")) + check_conn( + p, + conn, + AF_INET6, + SOCK_DGRAM, + udp6_addr, + (), + psutil.CONN_NONE, + ("all", "inet", "inet6", "udp", "udp6"), + ) def test_count(self): with create_sockets(): @@ -471,6 +499,7 @@ def check(cons, families, types_): with create_sockets(): from psutil._common import conn_tmap + for kind, groups in conn_tmap.items(): # XXX: SunOS does not retrieve UNIX sockets. if kind == 'unix' and not HAS_CONNECTIONS_UNIX: @@ -511,17 +540,18 @@ def test_multi_sockets_procs(self): for fname in fnames: wait_for_file(fname) - syscons = [x for x in psutil.net_connections(kind='all') if x.pid - in pids] + syscons = [ + x for x in psutil.net_connections(kind='all') if x.pid in pids + ] for pid in pids: - self.assertEqual(len([x for x in syscons if x.pid == pid]), - expected) + self.assertEqual( + len([x for x in syscons if x.pid == pid]), expected + ) p = psutil.Process(pid) self.assertEqual(len(p.connections('all')), expected) class TestMisc(PsutilTestCase): - def test_connection_constants(self): ints = [] strs = [] @@ -543,4 +573,5 @@ def test_connection_constants(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 9c54299a2..aa5a20abb 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -63,11 +63,10 @@ # Make sure code reflects what doc promises in terms of APIs # availability. -class TestAvailConstantsAPIs(PsutilTestCase): +class TestAvailConstantsAPIs(PsutilTestCase): def test_PROCFS_PATH(self): - self.assertEqual(hasattr(psutil, "PROCFS_PATH"), - LINUX or SUNOS or AIX) + self.assertEqual(hasattr(psutil, "PROCFS_PATH"), LINUX or SUNOS or AIX) def test_win_priority(self): ae = self.assertEqual @@ -92,8 +91,9 @@ def test_linux_ioprio_windows(self): ae(hasattr(psutil, "IOPRIO_LOW"), WINDOWS) ae(hasattr(psutil, "IOPRIO_VERYLOW"), WINDOWS) - @unittest.skipIf(GITHUB_ACTIONS and LINUX, - "unsupported on GITHUB_ACTIONS + LINUX") + @unittest.skipIf( + GITHUB_ACTIONS and LINUX, "unsupported on GITHUB_ACTIONS + LINUX" + ) def test_rlimit(self): ae = self.assertEqual ae(hasattr(psutil, "RLIM_INFINITY"), LINUX or FREEBSD) @@ -127,7 +127,6 @@ def test_rlimit(self): class TestAvailSystemAPIs(PsutilTestCase): - def test_win_service_iter(self): self.assertEqual(hasattr(psutil, "win_service_iter"), WINDOWS) @@ -135,27 +134,39 @@ def test_win_service_get(self): self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS) def test_cpu_freq(self): - self.assertEqual(hasattr(psutil, "cpu_freq"), - LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD) + self.assertEqual( + hasattr(psutil, "cpu_freq"), + LINUX or MACOS or WINDOWS or FREEBSD or OPENBSD, + ) def test_sensors_temperatures(self): self.assertEqual( - hasattr(psutil, "sensors_temperatures"), LINUX or FREEBSD) + hasattr(psutil, "sensors_temperatures"), LINUX or FREEBSD + ) def test_sensors_fans(self): self.assertEqual(hasattr(psutil, "sensors_fans"), LINUX) def test_battery(self): - self.assertEqual(hasattr(psutil, "sensors_battery"), - LINUX or WINDOWS or FREEBSD or MACOS) + self.assertEqual( + hasattr(psutil, "sensors_battery"), + LINUX or WINDOWS or FREEBSD or MACOS, + ) class TestAvailProcessAPIs(PsutilTestCase): - def test_environ(self): - self.assertEqual(hasattr(psutil.Process, "environ"), - LINUX or MACOS or WINDOWS or AIX or SUNOS or - FREEBSD or OPENBSD or NETBSD) + self.assertEqual( + hasattr(psutil.Process, "environ"), + LINUX + or MACOS + or WINDOWS + or AIX + or SUNOS + or FREEBSD + or OPENBSD + or NETBSD, + ) def test_uids(self): self.assertEqual(hasattr(psutil.Process, "uids"), POSIX) @@ -169,8 +180,9 @@ def test_terminal(self): def test_ionice(self): self.assertEqual(hasattr(psutil.Process, "ionice"), LINUX or WINDOWS) - @unittest.skipIf(GITHUB_ACTIONS and LINUX, - "unsupported on GITHUB_ACTIONS + LINUX") + @unittest.skipIf( + GITHUB_ACTIONS and LINUX, "unsupported on GITHUB_ACTIONS + LINUX" + ) def test_rlimit(self): self.assertEqual(hasattr(psutil.Process, "rlimit"), LINUX or FREEBSD) @@ -185,17 +197,19 @@ def test_num_handles(self): self.assertEqual(hasattr(psutil.Process, "num_handles"), WINDOWS) def test_cpu_affinity(self): - self.assertEqual(hasattr(psutil.Process, "cpu_affinity"), - LINUX or WINDOWS or FREEBSD) + self.assertEqual( + hasattr(psutil.Process, "cpu_affinity"), + LINUX or WINDOWS or FREEBSD, + ) def test_cpu_num(self): - self.assertEqual(hasattr(psutil.Process, "cpu_num"), - LINUX or FREEBSD or SUNOS) + self.assertEqual( + hasattr(psutil.Process, "cpu_num"), LINUX or FREEBSD or SUNOS + ) def test_memory_maps(self): hasit = hasattr(psutil.Process, "memory_maps") - self.assertEqual( - hasit, not (OPENBSD or NETBSD or AIX or MACOS)) + self.assertEqual(hasit, not (OPENBSD or NETBSD or AIX or MACOS)) # =================================================================== @@ -237,8 +251,9 @@ def test_cpu_count(self): self.assertIsInstance(psutil.cpu_count(), int) # TODO: remove this once 1892 is fixed - @unittest.skipIf(MACOS and platform.machine() == 'arm64', - "skipped due to #1892") + @unittest.skipIf( + MACOS and platform.machine() == 'arm64', "skipped due to #1892" + ) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_cpu_freq(self): if psutil.cpu_freq() is None: @@ -334,7 +349,6 @@ def test_users(self): class TestProcessWaitType(PsutilTestCase): - @unittest.skipIf(not POSIX, "not POSIX") def test_negative_signal(self): p = psutil.Process(self.spawn_testproc().pid) @@ -443,7 +457,11 @@ def test_all(self): except Exception: # noqa: BLE001 s = '\n' + '=' * 70 + '\n' s += "FAIL: name=test_%s, pid=%s, ret=%s\ninfo=%s\n" % ( - name, info['pid'], repr(value), info) + name, + info['pid'], + repr(value), + info, + ) s += '-' * 70 s += "\n%s" % traceback.format_exc() s = "\n".join((" " * 4) + i for i in s.splitlines()) + "\n" @@ -556,7 +574,8 @@ def ionice(self, ret, info): psutil.IOPRIO_VERYLOW, psutil.IOPRIO_LOW, psutil.IOPRIO_NORMAL, - psutil.IOPRIO_HIGH] + psutil.IOPRIO_HIGH, + ] self.assertIsInstance(ret, int) self.assertGreaterEqual(ret, 0) self.assertIn(ret, choices) @@ -730,8 +749,11 @@ def nice(self, ret, info): if POSIX: assert -20 <= ret <= 20, ret else: - priorities = [getattr(psutil, x) for x in dir(psutil) - if x.endswith('_PRIORITY_CLASS')] + priorities = [ + getattr(psutil, x) + for x in dir(psutil) + if x.endswith('_PRIORITY_CLASS') + ] self.assertIn(ret, priorities) if PY3: self.assertIsInstance(ret, enum.IntEnum) @@ -759,4 +781,5 @@ def environ(self, ret, info): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 5c62842a4..ada196bcd 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -61,7 +61,7 @@ SIOCGIFADDR = 0x8915 SIOCGIFCONF = 0x8912 SIOCGIFHWADDR = 0x8927 -SIOCGIFNETMASK = 0x891b +SIOCGIFNETMASK = 0x891B SIOCGIFBRDADDR = 0x8919 if LINUX: SECTOR_SIZE = 512 @@ -75,41 +75,47 @@ def get_ipv4_address(ifname): import fcntl + ifname = ifname[:15] if PY3: ifname = bytes(ifname, 'ascii') s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) with contextlib.closing(s): return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), - SIOCGIFADDR, - struct.pack('256s', ifname))[20:24]) + fcntl.ioctl(s.fileno(), SIOCGIFADDR, struct.pack('256s', ifname))[ + 20:24 + ] + ) def get_ipv4_netmask(ifname): import fcntl + ifname = ifname[:15] if PY3: ifname = bytes(ifname, 'ascii') s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) with contextlib.closing(s): return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), - SIOCGIFNETMASK, - struct.pack('256s', ifname))[20:24]) + fcntl.ioctl( + s.fileno(), SIOCGIFNETMASK, struct.pack('256s', ifname) + )[20:24] + ) def get_ipv4_broadcast(ifname): import fcntl + ifname = ifname[:15] if PY3: ifname = bytes(ifname, 'ascii') s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) with contextlib.closing(s): return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), - SIOCGIFBRDADDR, - struct.pack('256s', ifname))[20:24]) + fcntl.ioctl( + s.fileno(), SIOCGIFBRDADDR, struct.pack('256s', ifname) + )[20:24] + ) def get_ipv6_addresses(ifname): @@ -127,7 +133,7 @@ def get_ipv6_addresses(ifname): unformatted = all_fields[i][0] groups = [] for j in range(0, len(unformatted), 4): - groups.append(unformatted[j:j + 4]) + groups.append(unformatted[j : j + 4]) formatted = ":".join(groups) packed = socket.inet_pton(socket.AF_INET6, formatted) all_fields[i] = socket.inet_ntop(socket.AF_INET6, packed) @@ -136,18 +142,23 @@ def get_ipv6_addresses(ifname): def get_mac_address(ifname): import fcntl + ifname = ifname[:15] if PY3: ifname = bytes(ifname, 'ascii') s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) with contextlib.closing(s): info = fcntl.ioctl( - s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) + s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname) + ) if PY3: + def ord(x): return x + else: import __builtin__ + ord = __builtin__.ord return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] @@ -164,7 +175,8 @@ def free_swap(): nt = collections.namedtuple('free', 'total used free') return nt(int(total), int(used), int(free)) raise ValueError( - "can't find 'Swap' in 'free' output:\n%s" % '\n'.join(lines)) + "can't find 'Swap' in 'free' output:\n%s" % '\n'.join(lines) + ) def free_physmem(): @@ -179,13 +191,14 @@ def free_physmem(): lines = out.split('\n') for line in lines: if line.startswith('Mem'): - total, used, free, shared = \ - (int(x) for x in line.split()[1:5]) + total, used, free, shared = (int(x) for x in line.split()[1:5]) nt = collections.namedtuple( - 'free', 'total used free shared output') + 'free', 'total used free shared output' + ) return nt(total, used, free, shared, out) raise ValueError( - "can't find 'Mem' in 'free' output:\n%s" % '\n'.join(lines)) + "can't find 'Mem' in 'free' output:\n%s" % '\n'.join(lines) + ) def vmstat(stat): @@ -209,6 +222,7 @@ def mock_open_content(pairs): """Mock open() builtin and forces it to return a certain content for a given path. `pairs` is a {"path": "content", ...} dict. """ + def open_mock(name, *args, **kwargs): if name in pairs: content = pairs[name] @@ -233,6 +247,7 @@ 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 @@ -252,7 +267,6 @@ def open_mock(name, *args, **kwargs): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemVirtualMemoryAgainstFree(PsutilTestCase): - def test_total(self): cli_value = free_physmem().total psutil_value = psutil.virtual_memory().total @@ -268,15 +282,17 @@ def test_used(self): raise self.skipTest("old free version") cli_value = free_physmem().used psutil_value = psutil.virtual_memory().used - self.assertAlmostEqual(cli_value, psutil_value, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + cli_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_free(self): cli_value = free_physmem().free psutil_value = psutil.virtual_memory().free - self.assertAlmostEqual(cli_value, psutil_value, - delta=TOLERANCE_SYS_MEM) + self.assertAlmostEqual( + cli_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_shared(self): @@ -286,8 +302,11 @@ def test_shared(self): raise unittest.SkipTest("free does not support 'shared' column") psutil_value = psutil.virtual_memory().shared self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM, - msg='%s %s \n%s' % (free_value, psutil_value, free.output)) + free_value, + psutil_value, + delta=TOLERANCE_SYS_MEM, + msg='%s %s \n%s' % (free_value, psutil_value, free.output), + ) @retry_on_failure() def test_available(self): @@ -301,18 +320,21 @@ def test_available(self): free_value = int(lines[1].split()[-1]) psutil_value = psutil.virtual_memory().available self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM, - msg='%s %s \n%s' % (free_value, psutil_value, out)) + free_value, + psutil_value, + delta=TOLERANCE_SYS_MEM, + msg='%s %s \n%s' % (free_value, psutil_value, out), + ) @unittest.skipIf(not LINUX, "LINUX only") class TestSystemVirtualMemoryAgainstVmstat(PsutilTestCase): - def test_total(self): vmstat_value = vmstat('total memory') * 1024 psutil_value = psutil.virtual_memory().total self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_used(self): @@ -325,40 +347,44 @@ def test_used(self): vmstat_value = vmstat('used memory') * 1024 psutil_value = psutil.virtual_memory().used self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_free(self): vmstat_value = vmstat('free memory') * 1024 psutil_value = psutil.virtual_memory().free self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_buffers(self): vmstat_value = vmstat('buffer memory') * 1024 psutil_value = psutil.virtual_memory().buffers self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_active(self): vmstat_value = vmstat('active memory') * 1024 psutil_value = psutil.virtual_memory().active self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_inactive(self): vmstat_value = vmstat('inactive memory') * 1024 psutil_value = psutil.virtual_memory().inactive self.assertAlmostEqual( - vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM) + vmstat_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @unittest.skipIf(not LINUX, "LINUX only") class TestSystemVirtualMemoryMocks(PsutilTestCase): - 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 @@ -381,7 +407,8 @@ def test_warnings_on_misses(self): self.assertEqual(len(ws), 1) w = ws[0] self.assertIn( - "memory stats couldn't be determined", str(w.message)) + "memory stats couldn't be determined", str(w.message) + ) self.assertIn("cached", str(w.message)) self.assertIn("shared", str(w.message)) self.assertIn("active", str(w.message)) @@ -437,7 +464,8 @@ def test_avail_old_comes_from_kernel(self): self.assertEqual(ret.available, 6574984 * 1024) w = ws[0] self.assertIn( - "inactive memory stats couldn't be determined", str(w.message)) + "inactive memory stats couldn't be determined", str(w.message) + ) def test_avail_old_missing_fields(self): # Remove Active(file), Inactive(file) and SReclaimable @@ -461,7 +489,8 @@ def test_avail_old_missing_fields(self): self.assertEqual(ret.available, 2057400 * 1024 + 4818144 * 1024) w = ws[0] self.assertIn( - "inactive memory stats couldn't be determined", str(w.message)) + "inactive memory stats couldn't be determined", str(w.message) + ) def test_avail_old_missing_zoneinfo(self): # Remove /proc/zoneinfo file. Make sure fallback is used @@ -482,16 +511,19 @@ def test_avail_old_missing_zoneinfo(self): """).encode() with mock_open_content({"/proc/meminfo": content}): with mock_open_exception( - "/proc/zoneinfo", - IOError(errno.ENOENT, 'no such file or directory')): + "/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) + ret.available, 2057400 * 1024 + 4818144 * 1024 + ) w = ws[0] self.assertIn( "inactive memory stats couldn't be determined", - str(w.message)) + str(w.message), + ) def test_virtual_memory_mocked(self): # Emulate /proc/meminfo because neither vmstat nor free return slab. @@ -567,7 +599,6 @@ def test_virtual_memory_mocked(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemSwapMemory(PsutilTestCase): - @staticmethod def meminfo_has_swap_info(): """Return True if /proc/meminfo provides swap metrics.""" @@ -579,21 +610,24 @@ def test_total(self): free_value = free_swap().total psutil_value = psutil.swap_memory().total return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM) + free_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_used(self): free_value = free_swap().used psutil_value = psutil.swap_memory().used return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM) + free_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) @retry_on_failure() def test_free(self): free_value = free_swap().free psutil_value = psutil.swap_memory().free return self.assertAlmostEqual( - free_value, psutil_value, delta=TOLERANCE_SYS_MEM) + free_value, psutil_value, delta=TOLERANCE_SYS_MEM + ) def test_missing_sin_sout(self): with mock.patch('psutil._common.open', create=True) as m: @@ -605,15 +639,17 @@ def test_missing_sin_sout(self): w = ws[0] self.assertIn( "'sin' and 'sout' swap memory stats couldn't " - "be determined", str(w.message)) + "be determined", + str(w.message), + ) self.assertEqual(ret.sin, 0) self.assertEqual(ret.sout, 0) def test_no_vmstat_mocked(self): # see https://github.com/giampaolo/psutil/issues/722 with mock_open_exception( - "/proc/vmstat", - IOError(errno.ENOENT, 'no such file or directory')) as m: + "/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() @@ -623,7 +659,8 @@ def test_no_vmstat_mocked(self): self.assertIn( "'sin' and 'sout' swap memory stats couldn't " "be determined and were set to 0", - str(w.message)) + str(w.message), + ) self.assertEqual(ret.sin, 0) self.assertEqual(ret.sout, 0) @@ -637,6 +674,7 @@ def test_meminfo_against_sysinfo(self): swap = psutil.swap_memory() assert not m.called import psutil._psutil_linux as cext + _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo() total *= unit_multiplier free *= unit_multiplier @@ -659,7 +697,6 @@ def test_emulate_meminfo_has_no_metrics(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUTimes(PsutilTestCase): - def test_fields(self): fields = psutil.cpu_times()._fields kernel_ver = re.findall(r'\d+\.\d+\.\d+', os.uname()[2])[0] @@ -680,9 +717,10 @@ def test_fields(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUCountLogical(PsutilTestCase): - - @unittest.skipIf(not os.path.exists("/sys/devices/system/cpu/online"), - "/sys/devices/system/cpu/online does not exist") + @unittest.skipIf( + not os.path.exists("/sys/devices/system/cpu/online"), + "/sys/devices/system/cpu/online does not exist", + ) def test_against_sysdev_cpu_online(self): with open("/sys/devices/system/cpu/online") as f: value = f.read().strip() @@ -690,8 +728,10 @@ def test_against_sysdev_cpu_online(self): value = int(value.split('-')[1]) + 1 self.assertEqual(psutil.cpu_count(), value) - @unittest.skipIf(not os.path.exists("/sys/devices/system/cpu"), - "/sys/devices/system/cpu does not exist") + @unittest.skipIf( + not os.path.exists("/sys/devices/system/cpu"), + "/sys/devices/system/cpu does not exist", + ) def test_against_sysdev_cpu_num(self): ls = os.listdir("/sys/devices/system/cpu") count = len([x for x in ls if re.search(r"cpu\d+$", x) is not None]) @@ -710,11 +750,13 @@ def test_against_lscpu(self): def test_emulate_fallbacks(self): import psutil._pslinux + original = psutil._pslinux.cpu_count_logical() # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in # order to cause the parsing of /proc/cpuinfo and /proc/stat. with mock.patch( - 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: + 'psutil._pslinux.os.sysconf', side_effect=ValueError + ) as m: self.assertEqual(psutil._pslinux.cpu_count_logical(), original) assert m.called @@ -731,8 +773,9 @@ def test_emulate_fallbacks(self): with open('/proc/cpuinfo', 'rb') as f: cpuinfo_data = f.read() fake_file = io.BytesIO(cpuinfo_data) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(psutil._pslinux.cpu_count_logical(), original) # Finally, let's make /proc/cpuinfo return meaningless data; @@ -744,7 +787,6 @@ def test_emulate_fallbacks(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUCountCores(PsutilTestCase): - @unittest.skipIf(not which("lscpu"), "lscpu utility not available") def test_against_lscpu(self): out = sh("lscpu -p") @@ -773,7 +815,6 @@ def test_emulate_none(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUFrequency(PsutilTestCase): - @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_use_second_file(self): # https://github.com/giampaolo/psutil/issues/981 @@ -784,8 +825,9 @@ def path_exists_mock(path): return orig_exists(path) orig_exists = os.path.exists - with mock.patch("os.path.exists", side_effect=path_exists_mock, - create=True): + with mock.patch( + "os.path.exists", side_effect=path_exists_mock, create=True + ): assert psutil.cpu_freq() @unittest.skipIf(not HAS_CPU_FREQ, "not supported") @@ -816,14 +858,17 @@ def path_exists_mock(path): @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_data(self): def open_mock(name, *args, **kwargs): - if (name.endswith('/scaling_cur_freq') and - name.startswith("/sys/devices/system/cpu/cpufreq/policy")): + if name.endswith('/scaling_cur_freq') and name.startswith( + "/sys/devices/system/cpu/cpufreq/policy" + ): return io.BytesIO(b"500000") - elif (name.endswith('/scaling_min_freq') and - name.startswith("/sys/devices/system/cpu/cpufreq/policy")): + elif name.endswith('/scaling_min_freq') and name.startswith( + "/sys/devices/system/cpu/cpufreq/policy" + ): return io.BytesIO(b"600000") - elif (name.endswith('/scaling_max_freq') and - name.startswith("/sys/devices/system/cpu/cpufreq/policy")): + elif name.endswith('/scaling_max_freq') and name.startswith( + "/sys/devices/system/cpu/cpufreq/policy" + ): return io.BytesIO(b"700000") elif name == '/proc/cpuinfo': return io.BytesIO(b"cpu MHz : 500") @@ -833,8 +878,7 @@ def open_mock(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.patch( - 'os.path.exists', return_value=True): + with mock.patch('os.path.exists', return_value=True): freq = psutil.cpu_freq() self.assertEqual(freq.current, 500.0) # when /proc/cpuinfo is used min and max frequencies are not @@ -848,27 +892,32 @@ def open_mock(name, *args, **kwargs): def test_emulate_multi_cpu(self): def open_mock(name, *args, **kwargs): n = name - if (n.endswith('/scaling_cur_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): + if n.endswith('/scaling_cur_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy0" + ): return io.BytesIO(b"100000") - elif (n.endswith('/scaling_min_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): + elif n.endswith('/scaling_min_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy0" + ): return io.BytesIO(b"200000") - elif (n.endswith('/scaling_max_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): + elif n.endswith('/scaling_max_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy0" + ): return io.BytesIO(b"300000") - elif (n.endswith('/scaling_cur_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + elif n.endswith('/scaling_cur_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy1" + ): return io.BytesIO(b"400000") - elif (n.endswith('/scaling_min_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + elif n.endswith('/scaling_min_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy1" + ): return io.BytesIO(b"500000") - elif (n.endswith('/scaling_max_freq') and - n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + elif n.endswith('/scaling_max_freq') and n.startswith( + "/sys/devices/system/cpu/cpufreq/policy1" + ): return io.BytesIO(b"600000") elif name == '/proc/cpuinfo': - return io.BytesIO(b"cpu MHz : 100\n" - b"cpu MHz : 400") + return io.BytesIO(b"cpu MHz : 100\ncpu MHz : 400") else: return orig_open(name, *args, **kwargs) @@ -876,8 +925,9 @@ def open_mock(name, *args, **kwargs): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): with mock.patch('os.path.exists', return_value=True): - with mock.patch('psutil._pslinux.cpu_count_logical', - return_value=2): + with mock.patch( + 'psutil._pslinux.cpu_count_logical', return_value=2 + ): freq = psutil.cpu_freq(percpu=True) self.assertEqual(freq[0].current, 100.0) if freq[0].min != 0.0: @@ -907,15 +957,15 @@ def open_mock(name, *args, **kwargs): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): with mock.patch('os.path.exists', return_value=True): - with mock.patch('psutil._pslinux.cpu_count_logical', - return_value=1): + with mock.patch( + 'psutil._pslinux.cpu_count_logical', return_value=1 + ): freq = psutil.cpu_freq() self.assertEqual(freq.current, 200) @unittest.skipIf(not LINUX, "LINUX only") class TestSystemCPUStats(PsutilTestCase): - def test_ctx_switches(self): vmstat_value = vmstat("context switches") psutil_value = psutil.cpu_stats().ctx_switches @@ -929,7 +979,6 @@ def test_interrupts(self): @unittest.skipIf(not LINUX, "LINUX only") class TestLoadAvg(PsutilTestCase): - @unittest.skipIf(not HAS_GETLOADAVG, "not supported") def test_getloadavg(self): psutil_value = psutil.getloadavg() @@ -948,7 +997,6 @@ def test_getloadavg(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemNetIfAddrs(PsutilTestCase): - def test_ips(self): for name, addrs in psutil.net_if_addrs().items(): for addr in addrs: @@ -958,8 +1006,9 @@ def test_ips(self): self.assertEqual(addr.address, get_ipv4_address(name)) self.assertEqual(addr.netmask, get_ipv4_netmask(name)) if addr.broadcast is not None: - self.assertEqual(addr.broadcast, - get_ipv4_broadcast(name)) + self.assertEqual( + addr.broadcast, get_ipv4_broadcast(name) + ) else: self.assertEqual(get_ipv4_broadcast(name), '0.0.0.0') elif addr.family == socket.AF_INET6: @@ -990,7 +1039,6 @@ def test_ips(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemNetIfStats(PsutilTestCase): - @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") def test_against_ifconfig(self): for name, stats in psutil.net_if_stats().items(): @@ -1000,8 +1048,9 @@ def test_against_ifconfig(self): pass else: self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) - self.assertEqual(stats.mtu, - int(re.findall(r'(?i)MTU[: ](\d+)', out)[0])) + self.assertEqual( + stats.mtu, int(re.findall(r'(?i)MTU[: ](\d+)', out)[0]) + ) def test_mtu(self): for name, stats in psutil.net_if_stats().items(): @@ -1041,7 +1090,6 @@ def test_flags(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemNetIOCounters(PsutilTestCase): - @unittest.skipIf(not which("ifconfig"), "ifconfig utility not available") @retry_on_failure() def test_against_ifconfig(self): @@ -1049,17 +1097,21 @@ def ifconfig(nic): ret = {} out = sh("ifconfig %s" % nic) ret['packets_recv'] = int( - re.findall(r'RX packets[: ](\d+)', out)[0]) + re.findall(r'RX packets[: ](\d+)', out)[0] + ) ret['packets_sent'] = int( - re.findall(r'TX packets[: ](\d+)', out)[0]) + re.findall(r'TX packets[: ](\d+)', out)[0] + ) ret['errin'] = int(re.findall(r'errors[: ](\d+)', out)[0]) ret['errout'] = int(re.findall(r'errors[: ](\d+)', out)[1]) ret['dropin'] = int(re.findall(r'dropped[: ](\d+)', out)[0]) ret['dropout'] = int(re.findall(r'dropped[: ](\d+)', out)[1]) ret['bytes_recv'] = int( - re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0]) + re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0] + ) ret['bytes_sent'] = int( - re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0]) + re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0] + ) return ret nio = psutil.net_io_counters(pernic=True, nowrap=False) @@ -1069,26 +1121,33 @@ def ifconfig(nic): except RuntimeError: continue self.assertAlmostEqual( - stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5) + stats.bytes_recv, ifconfig_ret['bytes_recv'], delta=1024 * 5 + ) self.assertAlmostEqual( - stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5) + stats.bytes_sent, ifconfig_ret['bytes_sent'], delta=1024 * 5 + ) self.assertAlmostEqual( - stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024) + stats.packets_recv, ifconfig_ret['packets_recv'], delta=1024 + ) self.assertAlmostEqual( - stats.packets_sent, ifconfig_ret['packets_sent'], delta=1024) + stats.packets_sent, ifconfig_ret['packets_sent'], delta=1024 + ) self.assertAlmostEqual( - stats.errin, ifconfig_ret['errin'], delta=10) + stats.errin, ifconfig_ret['errin'], delta=10 + ) self.assertAlmostEqual( - stats.errout, ifconfig_ret['errout'], delta=10) + stats.errout, ifconfig_ret['errout'], delta=10 + ) self.assertAlmostEqual( - stats.dropin, ifconfig_ret['dropin'], delta=10) + stats.dropin, ifconfig_ret['dropin'], delta=10 + ) self.assertAlmostEqual( - stats.dropout, ifconfig_ret['dropout'], delta=10) + stats.dropout, ifconfig_ret['dropout'], delta=10 + ) @unittest.skipIf(not LINUX, "LINUX only") class TestSystemNetConnections(PsutilTestCase): - @mock.patch('psutil._pslinux.socket.inet_ntop', side_effect=ValueError) @mock.patch('psutil._pslinux.supports_ipv6', return_value=False) def test_emulate_ipv6_unsupported(self, supports_ipv6, inet_ntop): @@ -1120,7 +1179,6 @@ def test_emulate_unix(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemDiskPartitions(PsutilTestCase): - @unittest.skipIf(not hasattr(os, 'statvfs'), "os.statvfs() not available") @skip_on_not_implemented() def test_against_df(self): @@ -1141,10 +1199,12 @@ def df(path): usage = psutil.disk_usage(part.mountpoint) _, total, used, free = df(part.mountpoint) self.assertEqual(usage.total, total) - self.assertAlmostEqual(usage.free, free, - delta=TOLERANCE_DISK_USAGE) - self.assertAlmostEqual(usage.used, used, - delta=TOLERANCE_DISK_USAGE) + self.assertAlmostEqual( + usage.free, free, delta=TOLERANCE_DISK_USAGE + ) + self.assertAlmostEqual( + usage.used, used, delta=TOLERANCE_DISK_USAGE + ) def test_zfs_fs(self): # Test that ZFS partitions are returned. @@ -1159,11 +1219,13 @@ def test_zfs_fs(self): else: # No ZFS partitions on this system. Let's fake one. fake_file = io.StringIO(u("nodev\tzfs\n")) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m1: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m1: with mock.patch( - 'psutil._pslinux.cext.disk_partitions', - return_value=[('/dev/sdb3', '/', 'zfs', 'rw')]) as m2: + 'psutil._pslinux.cext.disk_partitions', + return_value=[('/dev/sdb3', '/', 'zfs', 'rw')], + ) as m2: ret = psutil.disk_partitions() assert m1.called assert m2.called @@ -1173,8 +1235,9 @@ def test_zfs_fs(self): def test_emulate_realpath_fail(self): # See: https://github.com/giampaolo/psutil/issues/1307 try: - with mock.patch('os.path.realpath', - return_value='/non/existent') as m: + with mock.patch( + 'os.path.realpath', return_value='/non/existent' + ) as m: with self.assertRaises(FileNotFoundError): psutil.disk_partitions() assert m.called @@ -1184,14 +1247,14 @@ def test_emulate_realpath_fail(self): @unittest.skipIf(not LINUX, "LINUX only") class TestSystemDiskIoCounters(PsutilTestCase): - def test_emulate_kernel_2_4(self): # Tests /proc/diskstats parsing format for 2.4 kernels, see: # https://github.com/giampaolo/psutil/issues/767 content = " 3 0 1 hda 2 3 4 5 6 7 8 9 10 11 12" with mock_open_content({'/proc/diskstats': content}): - with mock.patch('psutil._pslinux.is_storage_device', - return_value=True): + with mock.patch( + 'psutil._pslinux.is_storage_device', return_value=True + ): ret = psutil.disk_io_counters(nowrap=False) self.assertEqual(ret.read_count, 1) self.assertEqual(ret.read_merged_count, 2) @@ -1209,8 +1272,9 @@ def test_emulate_kernel_2_6_full(self): # https://github.com/giampaolo/psutil/issues/767 content = " 3 0 hda 1 2 3 4 5 6 7 8 9 10 11" with mock_open_content({"/proc/diskstats": content}): - with mock.patch('psutil._pslinux.is_storage_device', - return_value=True): + with mock.patch( + 'psutil._pslinux.is_storage_device', return_value=True + ): ret = psutil.disk_io_counters(nowrap=False) self.assertEqual(ret.read_count, 1) self.assertEqual(ret.read_merged_count, 2) @@ -1229,8 +1293,9 @@ def test_emulate_kernel_2_6_limited(self): # (instead of a disk). See: # https://github.com/giampaolo/psutil/issues/767 with mock_open_content({"/proc/diskstats": " 3 1 hda 1 2 3 4"}): - with mock.patch('psutil._pslinux.is_storage_device', - return_value=True): + with mock.patch( + 'psutil._pslinux.is_storage_device', return_value=True + ): ret = psutil.disk_io_counters(nowrap=False) self.assertEqual(ret.read_count, 1) self.assertEqual(ret.read_bytes, 2 * SECTOR_SIZE) @@ -1252,8 +1317,9 @@ def test_emulate_include_partitions(self): 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 """) with mock_open_content({"/proc/diskstats": content}): - with mock.patch('psutil._pslinux.is_storage_device', - return_value=False): + with mock.patch( + 'psutil._pslinux.is_storage_device', return_value=False + ): ret = psutil.disk_io_counters(perdisk=True, nowrap=False) self.assertEqual(len(ret), 2) self.assertEqual(ret['nvme0n1'].read_count, 1) @@ -1270,8 +1336,9 @@ def test_emulate_exclude_partitions(self): 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 """) with mock_open_content({"/proc/diskstats": content}): - with mock.patch('psutil._pslinux.is_storage_device', - return_value=False): + with mock.patch( + 'psutil._pslinux.is_storage_device', return_value=False + ): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) self.assertIsNone(ret) @@ -1284,8 +1351,11 @@ def is_storage_device(name): 3 0 nvme0n1p1 1 2 3 4 5 6 7 8 9 10 11 """) with mock_open_content({"/proc/diskstats": content}): - with mock.patch('psutil._pslinux.is_storage_device', - create=True, side_effect=is_storage_device): + with mock.patch( + 'psutil._pslinux.is_storage_device', + create=True, + side_effect=is_storage_device, + ): ret = psutil.disk_io_counters(perdisk=False, nowrap=False) self.assertEqual(ret.read_count, 1) self.assertEqual(ret.write_count, 5) @@ -1297,8 +1367,9 @@ def exists(path): return True wprocfs = psutil.disk_io_counters(perdisk=True) - with mock.patch('psutil._pslinux.os.path.exists', - create=True, side_effect=exists): + with mock.patch( + 'psutil._pslinux.os.path.exists', create=True, side_effect=exists + ): wsysfs = psutil.disk_io_counters(perdisk=True) self.assertEqual(len(wprocfs), len(wsysfs)) @@ -1306,14 +1377,14 @@ def test_emulate_not_impl(self): def exists(path): return False - with mock.patch('psutil._pslinux.os.path.exists', - create=True, side_effect=exists): + with mock.patch( + 'psutil._pslinux.os.path.exists', create=True, side_effect=exists + ): self.assertRaises(NotImplementedError, psutil.disk_io_counters) @unittest.skipIf(not LINUX, "LINUX only") class TestRootFsDeviceFinder(PsutilTestCase): - def setUp(self): dev = os.stat("/").st_dev self.major = os.major(dev) @@ -1325,8 +1396,9 @@ def test_call_methods(self): finder.ask_proc_partitions() else: self.assertRaises(FileNotFoundError, finder.ask_proc_partitions) - if os.path.exists("/sys/dev/block/%s:%s/uevent" % ( - self.major, self.minor)): + if os.path.exists( + "/sys/dev/block/%s:%s/uevent" % (self.major, self.minor) + ): finder.ask_sys_dev_block() else: self.assertRaises(FileNotFoundError, finder.ask_sys_dev_block) @@ -1340,8 +1412,9 @@ def test_comparisons(self): a = b = c = None if os.path.exists("/proc/partitions"): a = finder.ask_proc_partitions() - if os.path.exists("/sys/dev/block/%s:%s/uevent" % ( - self.major, self.minor)): + if os.path.exists( + "/sys/dev/block/%s:%s/uevent" % (self.major, self.minor) + ): b = finder.ask_sys_class_block() c = finder.ask_sys_dev_block() @@ -1362,8 +1435,9 @@ def test_against_findmnt(self): def test_disk_partitions_mocked(self): with mock.patch( - 'psutil._pslinux.cext.disk_partitions', - return_value=[('/dev/root', '/', 'ext4', 'rw')]) as m: + 'psutil._pslinux.cext.disk_partitions', + return_value=[('/dev/root', '/', 'ext4', 'rw')], + ) as m: part = psutil.disk_partitions()[0] assert m.called if not GITHUB_ACTIONS: @@ -1380,7 +1454,6 @@ def test_disk_partitions_mocked(self): @unittest.skipIf(not LINUX, "LINUX only") class TestMisc(PsutilTestCase): - def test_boot_time(self): vmstat_value = vmstat('boot time') psutil_value = psutil.boot_time() @@ -1413,7 +1486,8 @@ def open_mock(name, *args, **kwargs): self.assertRaises(IOError, psutil.cpu_percent, percpu=True) self.assertRaises(IOError, psutil.cpu_times_percent) self.assertRaises( - IOError, psutil.cpu_times_percent, percpu=True) + IOError, psutil.cpu_times_percent, percpu=True + ) psutil.PROCFS_PATH = my_procfs @@ -1436,11 +1510,11 @@ def open_mock(name, *args, **kwargs): f.write('cpu1 1 0 0 0 0 0 0 0 0 0\n') self.assertNotEqual(psutil.cpu_percent(), 0) - self.assertNotEqual( - sum(psutil.cpu_percent(percpu=True)), 0) + self.assertNotEqual(sum(psutil.cpu_percent(percpu=True)), 0) self.assertNotEqual(sum(psutil.cpu_times_percent()), 0) self.assertNotEqual( - sum(map(sum, psutil.cpu_times_percent(percpu=True))), 0) + sum(map(sum, psutil.cpu_times_percent(percpu=True))), 0 + ) finally: shutil.rmtree(my_procfs) reload_module(psutil) @@ -1488,9 +1562,7 @@ def test_cpu_steal_decrease(self): def test_boot_time_mocked(self): with mock.patch('psutil._common.open', create=True) as m: - self.assertRaises( - RuntimeError, - psutil._pslinux.boot_time) + self.assertRaises(RuntimeError, psutil._pslinux.boot_time) assert m.called def test_users(self): @@ -1552,7 +1624,6 @@ def test_pid_exists_no_proc_status(self): @unittest.skipIf(not LINUX, "LINUX only") @unittest.skipIf(not HAS_BATTERY, "no battery") class TestSensorsBattery(PsutilTestCase): - @unittest.skipIf(not which("acpi"), "acpi utility not available") def test_percent(self): out = sh("acpi -b") @@ -1573,7 +1644,8 @@ def open_mock(name, *args, **kwargs): with mock.patch(patch_point, side_effect=open_mock) as m: self.assertEqual(psutil.sensors_battery().power_plugged, True) self.assertEqual( - psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED) + psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + ) assert m.called def test_emulate_power_plugged_2(self): @@ -1628,10 +1700,10 @@ def test_emulate_power_undetermined(self): # Pretend we can't know whether the AC power cable not # connected (assert fallback to False). def open_mock(name, *args, **kwargs): - if name.startswith( - ('/sys/class/power_supply/AC0/online', - '/sys/class/power_supply/AC/online'), - ): + if name.startswith(( + '/sys/class/power_supply/AC0/online', + '/sys/class/power_supply/AC/online', + )): raise IOError(errno.ENOENT, "") elif name.startswith("/sys/class/power_supply/BAT0/status"): return io.BytesIO(b"???") @@ -1647,7 +1719,8 @@ def open_mock(name, *args, **kwargs): def test_emulate_energy_full_0(self): # Emulate a case where energy_full files returns 0. with mock_open_content( - {"/sys/class/power_supply/BAT0/energy_full": b"0"}) as m: + {"/sys/class/power_supply/BAT0/energy_full": b"0"} + ) as m: self.assertEqual(psutil.sensors_battery().percent, 0) assert m.called @@ -1655,32 +1728,35 @@ def test_emulate_energy_full_not_avail(self): # Emulate a case where energy_full file does not exist. # Expected fallback on /capacity. with mock_open_exception( - "/sys/class/power_supply/BAT0/energy_full", - IOError(errno.ENOENT, "")): + "/sys/class/power_supply/BAT0/energy_full", + IOError(errno.ENOENT, ""), + ): with mock_open_exception( - "/sys/class/power_supply/BAT0/charge_full", - IOError(errno.ENOENT, "")): + "/sys/class/power_supply/BAT0/charge_full", + IOError(errno.ENOENT, ""), + ): with mock_open_content( - {"/sys/class/power_supply/BAT0/capacity": b"88"}): + {"/sys/class/power_supply/BAT0/capacity": b"88"} + ): self.assertEqual(psutil.sensors_battery().percent, 88) def test_emulate_no_power(self): # Emulate a case where /AC0/online file nor /BAT0/status exist. with mock_open_exception( - "/sys/class/power_supply/AC/online", - IOError(errno.ENOENT, "")): + "/sys/class/power_supply/AC/online", IOError(errno.ENOENT, "") + ): with mock_open_exception( - "/sys/class/power_supply/AC0/online", - IOError(errno.ENOENT, "")): + "/sys/class/power_supply/AC0/online", IOError(errno.ENOENT, "") + ): with mock_open_exception( - "/sys/class/power_supply/BAT0/status", - IOError(errno.ENOENT, "")): + "/sys/class/power_supply/BAT0/status", + IOError(errno.ENOENT, ""), + ): self.assertIsNone(psutil.sensors_battery().power_plugged) @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsBatteryEmulated(PsutilTestCase): - def test_it(self): def open_mock(name, *args, **kwargs): if name.endswith("/energy_now"): @@ -1703,7 +1779,6 @@ def open_mock(name, *args, **kwargs): @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsTemperatures(PsutilTestCase): - def test_emulate_class_hwmon(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): @@ -1723,8 +1798,9 @@ def open_mock(name, *args, **kwargs): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): # Test case with /sys/class/hwmon - with mock.patch('glob.glob', - return_value=['/sys/class/hwmon/hwmon0/temp1']): + with mock.patch( + 'glob.glob', return_value=['/sys/class/hwmon/hwmon0/temp1'] + ): temp = psutil.sensors_temperatures()['name'][0] self.assertEqual(temp.label, 'label') self.assertEqual(temp.current, 30.0) @@ -1752,8 +1828,10 @@ def glob_mock(path): elif path == '/sys/class/thermal/thermal_zone*': return ['/sys/class/thermal/thermal_zone0'] elif path == '/sys/class/thermal/thermal_zone0/trip_point*': - return ['/sys/class/thermal/thermal_zone1/trip_point_0_type', - '/sys/class/thermal/thermal_zone1/trip_point_0_temp'] + return [ + '/sys/class/thermal/thermal_zone1/trip_point_0_type', + '/sys/class/thermal/thermal_zone1/trip_point_0_temp', + ] return [] orig_open = open @@ -1769,7 +1847,6 @@ def glob_mock(path): @unittest.skipIf(not LINUX, "LINUX only") class TestSensorsFans(PsutilTestCase): - def test_emulate_data(self): def open_mock(name, *args, **kwargs): if name.endswith('/name'): @@ -1784,8 +1861,9 @@ def open_mock(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.patch('glob.glob', - return_value=['/sys/class/hwmon/hwmon2/fan1']): + with mock.patch( + 'glob.glob', return_value=['/sys/class/hwmon/hwmon2/fan1'] + ): fan = psutil.sensors_fans()['name'][0] self.assertEqual(fan.label, 'label') self.assertEqual(fan.current, 2000) @@ -1798,19 +1876,18 @@ def open_mock(name, *args, **kwargs): @unittest.skipIf(not LINUX, "LINUX only") class TestProcess(PsutilTestCase): - @retry_on_failure() def test_parse_smaps_vs_memory_maps(self): sproc = self.spawn_testproc() uss, pss, swap = psutil._pslinux.Process(sproc.pid)._parse_smaps() maps = psutil.Process(sproc.pid).memory_maps(grouped=False) self.assertAlmostEqual( - uss, sum([x.private_dirty + x.private_clean for x in maps]), - delta=4096) - self.assertAlmostEqual( - pss, sum([x.pss for x in maps]), delta=4096) - self.assertAlmostEqual( - swap, sum([x.swap for x in maps]), delta=4096) + uss, + sum([x.private_dirty + x.private_clean for x in maps]), + delta=4096, + ) + self.assertAlmostEqual(pss, sum([x.pss for x in maps]), delta=4096) + self.assertAlmostEqual(swap, sum([x.swap for x in maps]), delta=4096) def test_parse_smaps_mocked(self): # See: https://github.com/giampaolo/psutil/issues/1222 @@ -1891,15 +1968,19 @@ def test_open_files_file_gone(self): with open(self.get_testfn(), 'w'): # give the kernel some time to see the new file call_until(p.open_files, "len(ret) != %i" % len(files)) - with mock.patch('psutil._pslinux.os.readlink', - side_effect=OSError(errno.ENOENT, "")) as m: + with mock.patch( + 'psutil._pslinux.os.readlink', + side_effect=OSError(errno.ENOENT, ""), + ) as m: files = p.open_files() assert not files assert m.called # also simulate the case where os.readlink() returns EINVAL # in which case psutil is supposed to 'continue' - with mock.patch('psutil._pslinux.os.readlink', - side_effect=OSError(errno.EINVAL, "")) as m: + with mock.patch( + 'psutil._pslinux.os.readlink', + side_effect=OSError(errno.EINVAL, ""), + ) as m: self.assertEqual(p.open_files(), []) assert m.called @@ -1913,8 +1994,9 @@ def test_open_files_fd_gone(self): # give the kernel some time to see the new file call_until(p.open_files, "len(ret) != %i" % len(files)) patch_point = 'builtins.open' if PY3 else '__builtin__.open' - with mock.patch(patch_point, - side_effect=IOError(errno.ENOENT, "")) as m: + with mock.patch( + patch_point, side_effect=IOError(errno.ENOENT, "") + ) as m: files = p.open_files() assert not files assert m.called @@ -1929,8 +2011,9 @@ def test_open_files_enametoolong(self): # give the kernel some time to see the new file call_until(p.open_files, "len(ret) != %i" % len(files)) patch_point = 'psutil._pslinux.os.readlink' - with mock.patch(patch_point, - side_effect=OSError(errno.ENAMETOOLONG, "")) as m: + with mock.patch( + patch_point, side_effect=OSError(errno.ENAMETOOLONG, "") + ) as m: with mock.patch("psutil._pslinux.debug"): files = p.open_files() assert not files @@ -1939,8 +2022,9 @@ def test_open_files_enametoolong(self): # --- mocked tests def test_terminal_mocked(self): - with mock.patch('psutil._pslinux._psposix.get_terminal_map', - return_value={}) as m: + with mock.patch( + 'psutil._pslinux._psposix.get_terminal_map', return_value={} + ) as m: self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) assert m.called @@ -1956,13 +2040,15 @@ def test_cmdline_mocked(self): # see: https://github.com/giampaolo/psutil/issues/639 p = psutil.Process() fake_file = io.StringIO(u('foo\x00bar\x00')) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(p.cmdline(), ['foo', 'bar']) assert m.called fake_file = io.StringIO(u('foo\x00bar\x00\x00')) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(p.cmdline(), ['foo', 'bar', '']) assert m.called @@ -1970,13 +2056,15 @@ def test_cmdline_spaces_mocked(self): # see: https://github.com/giampaolo/psutil/issues/1179 p = psutil.Process() fake_file = io.StringIO(u('foo bar ')) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(p.cmdline(), ['foo', 'bar']) assert m.called fake_file = io.StringIO(u('foo bar ')) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(p.cmdline(), ['foo', 'bar', '']) assert m.called @@ -1985,14 +2073,16 @@ def test_cmdline_mixed_separators(self): # 1179#issuecomment-552984549 p = psutil.Process() fake_file = io.StringIO(u('foo\x20bar\x00')) - with mock.patch('psutil._common.open', - return_value=fake_file, create=True) as m: + with mock.patch( + 'psutil._common.open', return_value=fake_file, create=True + ) as m: self.assertEqual(p.cmdline(), ['foo', 'bar']) assert m.called def test_readlink_path_deleted_mocked(self): - with mock.patch('psutil._pslinux.os.readlink', - return_value='/home/foo (deleted)'): + with mock.patch( + 'psutil._pslinux.os.readlink', return_value='/home/foo (deleted)' + ): self.assertEqual(psutil.Process().exe(), "/home/foo") self.assertEqual(psutil.Process().cwd(), "/home/foo") @@ -2026,8 +2116,9 @@ def open_mock_2(name, *args, **kwargs): self.assertRaises(psutil.AccessDenied, psutil.Process().threads) def test_exe_mocked(self): - with mock.patch('psutil._pslinux.readlink', - side_effect=OSError(errno.ENOENT, "")) as m: + with mock.patch( + 'psutil._pslinux.readlink', side_effect=OSError(errno.ENOENT, "") + ) as m: ret = psutil.Process().exe() assert m.called self.assertEqual(ret, "") @@ -2036,8 +2127,8 @@ def test_issue_1014(self): # Emulates a case where smaps file does not exist. In this case # wrap_exception decorator should not raise NoSuchProcess. with mock_open_exception( - '/proc/%s/smaps' % os.getpid(), - IOError(errno.ENOENT, "")) as m: + '/proc/%s/smaps' % os.getpid(), IOError(errno.ENOENT, "") + ) as m: p = psutil.Process() with self.assertRaises(FileNotFoundError): p.memory_maps() @@ -2048,10 +2139,12 @@ def test_rlimit_zombie(self): # Emulate a case where rlimit() raises ENOSYS, which may # happen in case of zombie process: # https://travis-ci.org/giampaolo/psutil/jobs/51368273 - with mock.patch("psutil._pslinux.prlimit", - side_effect=OSError(errno.ENOSYS, "")) as m1: - with mock.patch("psutil._pslinux.Process._is_zombie", - return_value=True) as m2: + with mock.patch( + "psutil._pslinux.prlimit", side_effect=OSError(errno.ENOSYS, "") + ) as m1: + with mock.patch( + "psutil._pslinux.Process._is_zombie", return_value=True + ) as m2: p = psutil.Process() p.name() with self.assertRaises(psutil.ZombieProcess) as exc: @@ -2063,48 +2156,48 @@ def test_rlimit_zombie(self): def test_stat_file_parsing(self): args = [ - "0", # pid + "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 - "0", # rt priority - "0", # policy - "7", # delayacct_blkio_ticks + "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 + "0", # rt priority + "0", # policy + "7", # delayacct_blkio_ticks ] content = " ".join(args).encode() with mock_open_content({"/proc/%s/stat" % os.getpid(): content}): @@ -2113,7 +2206,8 @@ def test_stat_file_parsing(self): self.assertEqual(p.status(), psutil.STATUS_ZOMBIE) self.assertEqual(p.ppid(), 1) self.assertEqual( - p.create_time(), 6 / CLOCK_TICKS + psutil.boot_time()) + p.create_time(), 6 / CLOCK_TICKS + psutil.boot_time() + ) cpu = p.cpu_times() self.assertEqual(cpu.user, 2 / CLOCK_TICKS) self.assertEqual(cpu.system, 3 / CLOCK_TICKS) @@ -2150,8 +2244,10 @@ def test_connections_enametoolong(self): # Simulate a case where /proc/{pid}/fd/{fd} symlink points to # a file with full path longer than PATH_MAX, see: # https://github.com/giampaolo/psutil/issues/1940 - with mock.patch('psutil._pslinux.os.readlink', - side_effect=OSError(errno.ENAMETOOLONG, "")) as m: + with mock.patch( + 'psutil._pslinux.os.readlink', + side_effect=OSError(errno.ENAMETOOLONG, ""), + ) as m: p = psutil.Process() with mock.patch("psutil._pslinux.debug"): assert not p.connections() @@ -2173,7 +2269,8 @@ def setUpClass(cls): def read_status_file(self, linestart): with psutil._psplatform.open_text( - '/proc/%s/status' % self.proc.pid) as f: + '/proc/%s/status' % self.proc.pid + ) as f: for line in f: line = line.strip() if line.startswith(linestart): @@ -2190,7 +2287,7 @@ def test_name(self): def test_status(self): value = self.read_status_file("State:") - value = value[value.find('(') + 1:value.rfind(')')] + value = value[value.find('(') + 1 : value.rfind(')')] value = value.replace(' ', '-') self.assertEqual(self.proc.status(), value) @@ -2224,7 +2321,8 @@ def test_cpu_affinity(self): if '-' in str(value): min_, max_ = map(int, value.split('-')) self.assertEqual( - self.proc.cpu_affinity(), list(range(min_, max_ + 1))) + self.proc.cpu_affinity(), list(range(min_, max_ + 1)) + ) def test_cpu_affinity_eligible_cpus(self): value = self.read_status_file("Cpus_allowed_list:") @@ -2243,7 +2341,6 @@ def test_cpu_affinity_eligible_cpus(self): @unittest.skipIf(not LINUX, "LINUX only") class TestUtils(PsutilTestCase): - def test_readlink(self): with mock.patch("os.readlink", return_value="foo (deleted)") as m: self.assertEqual(psutil._psplatform.readlink("bar"), "foo") @@ -2252,4 +2349,5 @@ def test_readlink(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_memleaks.py b/psutil/tests/test_memleaks.py index cd1b3f290..8a5b7f7f9 100755 --- a/psutil/tests/test_memleaks.py +++ b/psutil/tests/test_memleaks.py @@ -62,6 +62,7 @@ def fewtimes_if_linux(): """Decorator for those Linux functions which are implemented in pure Python, and which we want to run faster. """ + def decorator(fun): @functools.wraps(fun) def wrapper(self, *args, **kwargs): @@ -74,7 +75,9 @@ def wrapper(self, *args, **kwargs): self.__class__.times = before else: return fun(self, *args, **kwargs) + return wrapper + return decorator @@ -219,8 +222,7 @@ def test_cpu_affinity(self): def test_cpu_affinity_set(self): affinity = thisproc.cpu_affinity() self.execute(lambda: self.proc.cpu_affinity(affinity)) - self.execute_w_exc( - ValueError, lambda: self.proc.cpu_affinity([-1])) + self.execute_w_exc(ValueError, lambda: self.proc.cpu_affinity([-1])) @fewtimes_if_linux() def test_open_files(self): @@ -321,7 +323,6 @@ def call(): @unittest.skipIf(not WINDOWS, "WINDOWS only") class TestProcessDualImplementation(TestMemoryLeak): - def test_cmdline_peb_true(self): self.execute(lambda: cext.proc_cmdline(os.getpid(), use_peb=True)) @@ -365,8 +366,9 @@ def test_cpu_stats(self): @fewtimes_if_linux() # TODO: remove this once 1892 is fixed - @unittest.skipIf(MACOS and platform.machine() == 'arm64', - "skipped due to #1892") + @unittest.skipIf( + MACOS and platform.machine() == 'arm64', "skipped due to #1892" + ) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_cpu_freq(self): self.execute(psutil.cpu_freq) @@ -399,8 +401,10 @@ def test_disk_usage(self): def test_disk_partitions(self): self.execute(psutil.disk_partitions) - @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this Linux version') + @unittest.skipIf( + LINUX and not os.path.exists('/proc/diskstats'), + '/proc/diskstats not available on this Linux version', + ) @fewtimes_if_linux() def test_disk_io_counters(self): self.execute(lambda: psutil.disk_io_counters(nowrap=False)) @@ -488,4 +492,5 @@ def test_win_service_get_description(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index b3515de31..700c054f8 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -57,20 +57,20 @@ class TestSpecialMethods(PsutilTestCase): - def test_check_pid_range(self): with self.assertRaises(OverflowError): - psutil._psplatform.cext.check_pid_range(2 ** 128) + psutil._psplatform.cext.check_pid_range(2**128) with self.assertRaises(psutil.NoSuchProcess): - psutil.Process(2 ** 128) + psutil.Process(2**128) def test_process__repr__(self, func=repr): p = psutil.Process(self.spawn_testproc().pid) r = func(p) self.assertIn("psutil.Process", r) self.assertIn("pid=%s" % p.pid, r) - self.assertIn("name='%s'" % str(p.name()), - r.replace("name=u'", "name='")) + self.assertIn( + "name='%s'" % str(p.name()), r.replace("name=u'", "name='") + ) self.assertIn("status=", r) self.assertNotIn("exitcode=", r) p.terminate() @@ -79,22 +79,31 @@ def test_process__repr__(self, func=repr): self.assertIn("status='terminated'", r) self.assertIn("exitcode=", r) - with mock.patch.object(psutil.Process, "name", - side_effect=psutil.ZombieProcess(os.getpid())): + with mock.patch.object( + psutil.Process, + "name", + side_effect=psutil.ZombieProcess(os.getpid()), + ): p = psutil.Process() r = func(p) self.assertIn("pid=%s" % p.pid, r) self.assertIn("status='zombie'", r) self.assertNotIn("name=", r) - with mock.patch.object(psutil.Process, "name", - side_effect=psutil.NoSuchProcess(os.getpid())): + with mock.patch.object( + psutil.Process, + "name", + side_effect=psutil.NoSuchProcess(os.getpid()), + ): p = psutil.Process() r = func(p) self.assertIn("pid=%s" % p.pid, r) self.assertIn("terminated", r) self.assertNotIn("name=", r) - with mock.patch.object(psutil.Process, "name", - side_effect=psutil.AccessDenied(os.getpid())): + with mock.patch.object( + psutil.Process, + "name", + side_effect=psutil.AccessDenied(os.getpid()), + ): p = psutil.Process() r = func(p) self.assertIn("pid=%s" % p.pid, r) @@ -112,68 +121,79 @@ def test_error__str__(self): def test_no_such_process__repr__(self): self.assertEqual( repr(psutil.NoSuchProcess(321)), - "psutil.NoSuchProcess(pid=321, msg='process no longer exists')") + "psutil.NoSuchProcess(pid=321, msg='process no longer exists')", + ) self.assertEqual( repr(psutil.NoSuchProcess(321, name="name", msg="msg")), - "psutil.NoSuchProcess(pid=321, name='name', msg='msg')") + "psutil.NoSuchProcess(pid=321, name='name', msg='msg')", + ) def test_no_such_process__str__(self): self.assertEqual( str(psutil.NoSuchProcess(321)), - "process no longer exists (pid=321)") + "process no longer exists (pid=321)", + ) self.assertEqual( str(psutil.NoSuchProcess(321, name="name", msg="msg")), - "msg (pid=321, name='name')") + "msg (pid=321, name='name')", + ) def test_zombie_process__repr__(self): self.assertEqual( repr(psutil.ZombieProcess(321)), 'psutil.ZombieProcess(pid=321, msg="PID still ' - 'exists but it\'s a zombie")') + 'exists but it\'s a zombie")', + ) self.assertEqual( repr(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")), - "psutil.ZombieProcess(pid=321, ppid=320, name='name', msg='foo')") + "psutil.ZombieProcess(pid=321, ppid=320, name='name', msg='foo')", + ) def test_zombie_process__str__(self): self.assertEqual( str(psutil.ZombieProcess(321)), - "PID still exists but it's a zombie (pid=321)") + "PID still exists but it's a zombie (pid=321)", + ) self.assertEqual( str(psutil.ZombieProcess(321, name="name", ppid=320, msg="foo")), - "foo (pid=321, ppid=320, name='name')") + "foo (pid=321, ppid=320, name='name')", + ) def test_access_denied__repr__(self): self.assertEqual( - repr(psutil.AccessDenied(321)), - "psutil.AccessDenied(pid=321)") + repr(psutil.AccessDenied(321)), "psutil.AccessDenied(pid=321)" + ) self.assertEqual( repr(psutil.AccessDenied(321, name="name", msg="msg")), - "psutil.AccessDenied(pid=321, name='name', msg='msg')") + "psutil.AccessDenied(pid=321, name='name', msg='msg')", + ) def test_access_denied__str__(self): - self.assertEqual( - str(psutil.AccessDenied(321)), - "(pid=321)") + self.assertEqual(str(psutil.AccessDenied(321)), "(pid=321)") self.assertEqual( str(psutil.AccessDenied(321, name="name", msg="msg")), - "msg (pid=321, name='name')") + "msg (pid=321, name='name')", + ) def test_timeout_expired__repr__(self): self.assertEqual( repr(psutil.TimeoutExpired(5)), - "psutil.TimeoutExpired(seconds=5, msg='timeout after 5 seconds')") + "psutil.TimeoutExpired(seconds=5, msg='timeout after 5 seconds')", + ) self.assertEqual( repr(psutil.TimeoutExpired(5, pid=321, name="name")), "psutil.TimeoutExpired(pid=321, name='name', seconds=5, " - "msg='timeout after 5 seconds')") + "msg='timeout after 5 seconds')", + ) def test_timeout_expired__str__(self): self.assertEqual( - str(psutil.TimeoutExpired(5)), - "timeout after 5 seconds") + str(psutil.TimeoutExpired(5)), "timeout after 5 seconds" + ) self.assertEqual( str(psutil.TimeoutExpired(5, pid=321, name="name")), - "timeout after 5 seconds (pid=321, name='name')") + "timeout after 5 seconds (pid=321, name='name')", + ) def test_process__eq__(self): p1 = psutil.Process() @@ -194,12 +214,16 @@ def test_process__hash__(self): class TestMisc(PsutilTestCase): - def test__all__(self): dir_psutil = dir(psutil) for name in dir_psutil: - if name in ('long', 'tests', 'test', 'PermissionError', - 'ProcessLookupError'): + if name in ( + 'long', + 'tests', + 'test', + 'PermissionError', + 'ProcessLookupError', + ): continue if not name.startswith('_'): try: @@ -209,8 +233,10 @@ def test__all__(self): fun = getattr(psutil, name) if fun is None: continue - if (fun.__doc__ is not None and - 'deprecated' not in fun.__doc__.lower()): + if ( + fun.__doc__ is not None + and 'deprecated' not in fun.__doc__.lower() + ): raise self.fail('%r not in psutil.__all__' % name) # Import 'star' will break if __all__ is inconsistent, see: @@ -221,8 +247,9 @@ def test__all__(self): self.assertIn(name, dir_psutil) def test_version(self): - self.assertEqual('.'.join([str(x) for x in psutil.version_info]), - psutil.__version__) + self.assertEqual( + '.'.join([str(x) for x in psutil.version_info]), psutil.__version__ + ) def test_process_as_dict_no_new_names(self): # See https://github.com/giampaolo/psutil/issues/813 @@ -266,16 +293,19 @@ def check(ret): def test_ad_on_process_creation(self): # We are supposed to be able to instantiate Process also in case # of zombie processes or access denied. - with mock.patch.object(psutil.Process, 'create_time', - side_effect=psutil.AccessDenied) as meth: + with mock.patch.object( + psutil.Process, 'create_time', side_effect=psutil.AccessDenied + ) as meth: psutil.Process() assert meth.called - with mock.patch.object(psutil.Process, 'create_time', - side_effect=psutil.ZombieProcess(1)) as meth: + with mock.patch.object( + psutil.Process, 'create_time', side_effect=psutil.ZombieProcess(1) + ) as meth: psutil.Process() assert meth.called - with mock.patch.object(psutil.Process, 'create_time', - side_effect=ValueError) as meth: + with mock.patch.object( + psutil.Process, 'create_time', side_effect=ValueError + ) as meth: with self.assertRaises(ValueError): psutil.Process() assert meth.called @@ -283,7 +313,8 @@ def test_ad_on_process_creation(self): def test_sanity_version_check(self): # see: https://github.com/giampaolo/psutil/issues/564 with mock.patch( - "psutil._psplatform.cext.version", return_value="0.0.0"): + "psutil._psplatform.cext.version", return_value="0.0.0" + ): with self.assertRaises(ImportError) as cm: reload_module(psutil) self.assertIn("version conflict", str(cm.exception).lower()) @@ -295,7 +326,6 @@ def test_sanity_version_check(self): class TestMemoizeDecorator(PsutilTestCase): - def setUp(self): self.calls = [] @@ -311,14 +341,15 @@ def run_against(self, obj, expected_retval=None): # with args for _ in range(2): ret = obj(1) - self.assertEqual(self.calls, [((), {}), ((1, ), {})]) + self.assertEqual(self.calls, [((), {}), ((1,), {})]) if expected_retval is not None: self.assertEqual(ret, expected_retval) # with args + kwargs for _ in range(2): ret = obj(1, bar=2) self.assertEqual( - self.calls, [((), {}), ((1, ), {}), ((1, ), {'bar': 2})]) + self.calls, [((), {}), ((1,), {}), ((1,), {'bar': 2})] + ) if expected_retval is not None: self.assertEqual(ret, expected_retval) # clear cache @@ -412,13 +443,13 @@ def foo(*args, **kwargs): # with args for _ in range(2): ret = foo(1) - expected = ((1, ), {}) + expected = ((1,), {}) self.assertEqual(ret, expected) self.assertEqual(len(calls), 2) # with args + kwargs for _ in range(2): ret = foo(1, bar=2) - expected = ((1, ), {'bar': 2}) + expected = ((1,), {'bar': 2}) self.assertEqual(ret, expected) self.assertEqual(len(calls), 3) # clear cache @@ -432,10 +463,8 @@ def foo(*args, **kwargs): class TestCommonModule(PsutilTestCase): - def test_memoize_when_activated(self): class Foo: - @memoize_when_activated def foo(self): calls.append(None) @@ -464,15 +493,18 @@ def test_parse_environ_block(self): def k(s): return s.upper() if WINDOWS else s - self.assertEqual(parse_environ_block("a=1\0"), - {k("a"): "1"}) - self.assertEqual(parse_environ_block("a=1\0b=2\0\0"), - {k("a"): "1", k("b"): "2"}) - self.assertEqual(parse_environ_block("a=1\0b=\0\0"), - {k("a"): "1", k("b"): ""}) + self.assertEqual(parse_environ_block("a=1\0"), {k("a"): "1"}) + self.assertEqual( + parse_environ_block("a=1\0b=2\0\0"), {k("a"): "1", k("b"): "2"} + ) + self.assertEqual( + parse_environ_block("a=1\0b=\0\0"), {k("a"): "1", k("b"): ""} + ) # ignore everything after \0\0 - self.assertEqual(parse_environ_block("a=1\0b=2\0\0c=3\0"), - {k("a"): "1", k("b"): "2"}) + self.assertEqual( + parse_environ_block("a=1\0b=2\0\0c=3\0"), + {k("a"): "1", k("b"): "2"}, + ) # ignore everything that is not an assignment self.assertEqual(parse_environ_block("xxx\0a=1\0"), {k("a"): "1"}) self.assertEqual(parse_environ_block("a=1\0=b=2\0"), {k("a"): "1"}) @@ -488,21 +520,25 @@ def test_supports_ipv6(self): assert not supports_ipv6() supports_ipv6.cache_clear() - with mock.patch('psutil._common.socket.socket', - side_effect=socket.error) as s: + with mock.patch( + 'psutil._common.socket.socket', side_effect=socket.error + ) as s: assert not supports_ipv6() assert s.called supports_ipv6.cache_clear() - with mock.patch('psutil._common.socket.socket', - side_effect=socket.gaierror) as s: + with mock.patch( + 'psutil._common.socket.socket', side_effect=socket.gaierror + ) as s: assert not supports_ipv6() supports_ipv6.cache_clear() assert s.called supports_ipv6.cache_clear() - with mock.patch('psutil._common.socket.socket.bind', - side_effect=socket.gaierror) as s: + with mock.patch( + 'psutil._common.socket.socket.bind', + side_effect=socket.gaierror, + ) as s: assert not supports_ipv6() supports_ipv6.cache_clear() assert s.called @@ -518,14 +554,17 @@ def test_isfile_strict(self): this_file = os.path.abspath(__file__) assert isfile_strict(this_file) assert not isfile_strict(os.path.dirname(this_file)) - with mock.patch('psutil._common.os.stat', - side_effect=OSError(errno.EPERM, "foo")): + with mock.patch( + 'psutil._common.os.stat', side_effect=OSError(errno.EPERM, "foo") + ): self.assertRaises(OSError, isfile_strict, this_file) - with mock.patch('psutil._common.os.stat', - side_effect=OSError(errno.EACCES, "foo")): + with mock.patch( + 'psutil._common.os.stat', side_effect=OSError(errno.EACCES, "foo") + ): self.assertRaises(OSError, isfile_strict, this_file) - with mock.patch('psutil._common.os.stat', - side_effect=OSError(errno.ENOENT, "foo")): + with mock.patch( + 'psutil._common.os.stat', side_effect=OSError(errno.ENOENT, "foo") + ): assert not isfile_strict(this_file) with mock.patch('psutil._common.stat.S_ISREG', return_value=False): assert not isfile_strict(this_file) @@ -580,7 +619,6 @@ def test_cat_bcat(self): class TestWrapNumbers(PsutilTestCase): - def setUp(self): wrap_numbers.cache_clear() @@ -611,36 +649,44 @@ def test_wrap(self): self.assertEqual(wrap_numbers(input, 'disk_io'), input) # first wrap restarts from 10 input = {'disk1': nt(100, 100, 10)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(100, 100, 110)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 110)} + ) # then it remains the same input = {'disk1': nt(100, 100, 10)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(100, 100, 110)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 110)} + ) # then it goes up input = {'disk1': nt(100, 100, 90)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(100, 100, 190)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 190)} + ) # then it wraps again input = {'disk1': nt(100, 100, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(100, 100, 210)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 210)} + ) # and remains the same input = {'disk1': nt(100, 100, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(100, 100, 210)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(100, 100, 210)} + ) # now wrap another num input = {'disk1': nt(50, 100, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(150, 100, 210)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(150, 100, 210)} + ) # and again input = {'disk1': nt(40, 100, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(190, 100, 210)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(190, 100, 210)} + ) # keep it the same input = {'disk1': nt(40, 100, 20)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(190, 100, 210)}) + self.assertEqual( + wrap_numbers(input, 'disk_io'), {'disk1': nt(190, 100, 210)} + ) def test_changing_keys(self): # Emulate a case where the second call to disk_io() @@ -648,54 +694,54 @@ def test_changing_keys(self): # disappears on the third call. input = {'disk1': nt(5, 5, 5)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) - input = {'disk1': nt(5, 5, 5), - 'disk2': nt(7, 7, 7)} + input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) input = {'disk1': nt(8, 8, 8)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) def test_changing_keys_w_wrap(self): - input = {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 100)} + input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) # disk 2 wraps - input = {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 10)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 110)}) + input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} + self.assertEqual( + wrap_numbers(input, 'disk_io'), + {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 110)}, + ) # disk 2 disappears input = {'disk1': nt(50, 50, 50)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) # then it appears again; the old wrap is supposed to be # gone. - input = {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 100)} + input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) # remains the same - input = {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 100)} + input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 100)} self.assertEqual(wrap_numbers(input, 'disk_io'), input) # and then wraps again - input = {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 10)} - self.assertEqual(wrap_numbers(input, 'disk_io'), - {'disk1': nt(50, 50, 50), - 'disk2': nt(100, 100, 110)}) + input = {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 10)} + self.assertEqual( + wrap_numbers(input, 'disk_io'), + {'disk1': nt(50, 50, 50), 'disk2': nt(100, 100, 110)}, + ) def test_real_data(self): - d = {'nvme0n1': (300, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), - 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), - 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), - 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348)} + d = { + 'nvme0n1': (300, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), + 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), + 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), + 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), + } self.assertEqual(wrap_numbers(d, 'disk_io'), d) self.assertEqual(wrap_numbers(d, 'disk_io'), d) # decrease this ↓ - d = {'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), - 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), - 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), - 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348)} + d = { + 'nvme0n1': (100, 508, 640, 1571, 5970, 1987, 2049, 451751, 47048), + 'nvme0n1p1': (1171, 2, 5600256, 1024, 516, 0, 0, 0, 8), + 'nvme0n1p2': (54, 54, 2396160, 5165056, 4, 24, 30, 1207, 28), + 'nvme0n1p3': (2389, 4539, 5154, 150, 4828, 1844, 2019, 398, 348), + } out = wrap_numbers(d, 'disk_io') self.assertEqual(out['nvme0n1'][0], 400) @@ -718,7 +764,8 @@ def test_cache_call_twice(self): self.assertEqual(cache[0], {'disk_io': input}) self.assertEqual( cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}) + {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}, + ) self.assertEqual(cache[2], {'disk_io': {}}) def test_cache_wrap(self): @@ -733,17 +780,25 @@ def test_cache_wrap(self): self.assertEqual(cache[0], {'disk_io': input}) self.assertEqual( cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100}}) + {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 100}}, + ) self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}}) def check_cache_info(): cache = wrap_numbers.cache_info() self.assertEqual( cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, - ('disk1', 2): 100}}) - self.assertEqual(cache[2], - {'disk_io': {'disk1': set([('disk1', 2)])}}) + { + 'disk_io': { + ('disk1', 0): 0, + ('disk1', 1): 0, + ('disk1', 2): 100, + } + }, + ) + self.assertEqual( + cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}} + ) # then it remains the same input = {'disk1': nt(100, 100, 10)} @@ -766,20 +821,21 @@ def check_cache_info(): self.assertEqual(cache[0], {'disk_io': input}) self.assertEqual( cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190}}) + {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 190}}, + ) self.assertEqual(cache[2], {'disk_io': {'disk1': set([('disk1', 2)])}}) def test_cache_changing_keys(self): input = {'disk1': nt(5, 5, 5)} wrap_numbers(input, 'disk_io') - input = {'disk1': nt(5, 5, 5), - 'disk2': nt(7, 7, 7)} + input = {'disk1': nt(5, 5, 5), 'disk2': nt(7, 7, 7)} wrap_numbers(input, 'disk_io') cache = wrap_numbers.cache_info() self.assertEqual(cache[0], {'disk_io': input}) self.assertEqual( cache[1], - {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}) + {'disk_io': {('disk1', 0): 0, ('disk1', 1): 0, ('disk1', 2): 0}}, + ) self.assertEqual(cache[2], {'disk_io': {}}) def test_cache_clear(self): @@ -818,8 +874,9 @@ def test_cache_clear_public_apis(self): # =================================================================== -@unittest.skipIf(not os.path.exists(SCRIPTS_DIR), - "can't locate scripts directory") +@unittest.skipIf( + not os.path.exists(SCRIPTS_DIR), "can't locate scripts directory" +) class TestScripts(PsutilTestCase): """Tests for scripts in the "scripts" directory.""" @@ -854,8 +911,10 @@ def test_coverage(self): if name.endswith('.py'): if 'test_' + os.path.splitext(name)[0] not in meths: # self.assert_stdout(name) - raise self.fail('no test defined for %r script' - % os.path.join(SCRIPTS_DIR, name)) + raise self.fail( + 'no test defined for %r script' + % os.path.join(SCRIPTS_DIR, name) + ) @unittest.skipIf(not POSIX, "POSIX only") def test_executable(self): @@ -951,4 +1010,5 @@ def test_sensors(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py index dc0dd6baa..8fce8ae08 100755 --- a/psutil/tests/test_osx.py +++ b/psutil/tests/test_osx.py @@ -53,7 +53,6 @@ def vm_stat(field): @unittest.skipIf(not MACOS, "MACOS only") class TestProcess(PsutilTestCase): - @classmethod def setUpClass(cls): cls.pid = spawn_testproc().pid @@ -69,11 +68,11 @@ def test_process_create_time(self): year = start_ps.split(' ')[-1] start_psutil = psutil.Process(self.pid).create_time() self.assertEqual( - hhmmss, - time.strftime("%H:%M:%S", time.localtime(start_psutil))) + hhmmss, time.strftime("%H:%M:%S", time.localtime(start_psutil)) + ) self.assertEqual( - year, - time.strftime("%Y", time.localtime(start_psutil))) + year, time.strftime("%Y", time.localtime(start_psutil)) + ) @unittest.skipIf(not MACOS, "MACOS only") @@ -103,10 +102,12 @@ def df(path): dev, total, used, free = df(part.mountpoint) self.assertEqual(part.device, dev) self.assertEqual(usage.total, total) - self.assertAlmostEqual(usage.free, free, - delta=TOLERANCE_DISK_USAGE) - self.assertAlmostEqual(usage.used, used, - delta=TOLERANCE_DISK_USAGE) + self.assertAlmostEqual( + usage.free, free, delta=TOLERANCE_DISK_USAGE + ) + self.assertAlmostEqual( + usage.used, used, delta=TOLERANCE_DISK_USAGE + ) # --- cpu @@ -123,11 +124,14 @@ def test_cpu_count_cores(self): def test_cpu_freq(self): freq = psutil.cpu_freq() self.assertEqual( - freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency")) + freq.current * 1000 * 1000, sysctl("sysctl hw.cpufrequency") + ) self.assertEqual( - freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min")) + freq.min * 1000 * 1000, sysctl("sysctl hw.cpufrequency_min") + ) self.assertEqual( - freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max")) + freq.max * 1000 * 1000, sysctl("sysctl hw.cpufrequency_max") + ) # --- virtual mem @@ -183,8 +187,9 @@ def test_net_if_stats(self): pass else: self.assertEqual(stats.isup, 'RUNNING' in out, msg=out) - self.assertEqual(stats.mtu, - int(re.findall(r'mtu (\d+)', out)[0])) + self.assertEqual( + stats.mtu, int(re.findall(r'mtu (\d+)', out)[0]) + ) # --- sensors_battery @@ -201,4 +206,5 @@ def test_sensors_battery(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py index eaaf0d1c2..53852cef8 100755 --- a/psutil/tests/test_posix.py +++ b/psutil/tests/test_posix.py @@ -85,6 +85,7 @@ def ps(fmt, pid=None): else: return all_output[0] + # ps "-o" field names differ wildly between platforms. # "comm" means "only executable name" but is not available on BSD platforms. # "args" means "command with all its arguments", and is also not available @@ -134,8 +135,9 @@ class TestProcess(PsutilTestCase): @classmethod def setUpClass(cls): - cls.pid = spawn_testproc([PYTHON_EXE, "-E", "-O"], - stdin=subprocess.PIPE).pid + cls.pid = spawn_testproc( + [PYTHON_EXE, "-E", "-O"], stdin=subprocess.PIPE + ).pid @classmethod def tearDownClass(cls): @@ -210,10 +212,10 @@ def test_name_long(self): # full name from the cmdline. name = "long-program-name" cmdline = ["long-program-name-extended", "foo", "bar"] - with mock.patch("psutil._psplatform.Process.name", - return_value=name): - with mock.patch("psutil._psplatform.Process.cmdline", - return_value=cmdline): + with mock.patch("psutil._psplatform.Process.name", return_value=name): + with mock.patch( + "psutil._psplatform.Process.cmdline", return_value=cmdline + ): p = psutil.Process() self.assertEqual(p.name(), "long-program-name-extended") @@ -222,10 +224,11 @@ def test_name_long_cmdline_ad_exc(self): # AccessDenied in which case psutil is supposed to return # the truncated name instead of crashing. name = "long-program-name" - with mock.patch("psutil._psplatform.Process.name", - return_value=name): - with mock.patch("psutil._psplatform.Process.cmdline", - side_effect=psutil.AccessDenied(0, "")): + with mock.patch("psutil._psplatform.Process.name", return_value=name): + with mock.patch( + "psutil._psplatform.Process.cmdline", + side_effect=psutil.AccessDenied(0, ""), + ): p = psutil.Process() self.assertEqual(p.name(), "long-program-name") @@ -233,10 +236,11 @@ def test_name_long_cmdline_nsp_exc(self): # Same as above but emulates a case where cmdline() raises NSP # which is supposed to propagate. name = "long-program-name" - with mock.patch("psutil._psplatform.Process.name", - return_value=name): - with mock.patch("psutil._psplatform.Process.cmdline", - side_effect=psutil.NoSuchProcess(0, "")): + with mock.patch("psutil._psplatform.Process.name", return_value=name): + with mock.patch( + "psutil._psplatform.Process.cmdline", + side_effect=psutil.NoSuchProcess(0, ""), + ): p = psutil.Process() self.assertRaises(psutil.NoSuchProcess, p.name) @@ -245,12 +249,14 @@ def test_create_time(self): time_ps = ps('start', self.pid) time_psutil = psutil.Process(self.pid).create_time() time_psutil_tstamp = datetime.datetime.fromtimestamp( - time_psutil).strftime("%H:%M:%S") + time_psutil + ).strftime("%H:%M:%S") # sometimes ps shows the time rounded up instead of down, so we check # for both possible values round_time_psutil = round(time_psutil) round_time_psutil_tstamp = datetime.datetime.fromtimestamp( - round_time_psutil).strftime("%H:%M:%S") + round_time_psutil + ).strftime("%H:%M:%S") self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) def test_exe(self): @@ -265,7 +271,7 @@ def test_exe(self): # "/usr/local/bin/python" # We do not want to consider this difference in accuracy # an error. - adjusted_ps_pathname = ps_pathname[:len(ps_pathname)] + adjusted_ps_pathname = ps_pathname[: len(ps_pathname)] self.assertEqual(ps_pathname, adjusted_ps_pathname) # On macOS the official python installer exposes a python wrapper that @@ -309,8 +315,9 @@ def test_pids(self): # There will often be one more process in pids_ps for ps itself if len(pids_ps) - len(pids_psutil) > 1: - difference = [x for x in pids_psutil if x not in pids_ps] + \ - [x for x in pids_ps if x not in pids_psutil] + difference = [x for x in pids_psutil if x not in pids_ps] + [ + x for x in pids_ps if x not in pids_psutil + ] raise self.fail("difference: " + str(difference)) # for some reason ifconfig -a does not report all interfaces @@ -326,8 +333,9 @@ def test_nic_names(self): break else: raise self.fail( - "couldn't find %s nic in 'ifconfig -a' output\n%s" % ( - nic, output)) + "couldn't find %s nic in 'ifconfig -a' output\n%s" + % (nic, output) + ) # @unittest.skipIf(CI_TESTING and not psutil.users(), "unreliable on CI") @retry_on_failure() @@ -375,46 +383,56 @@ def test_users_started(self): if not tstamp: raise unittest.SkipTest( - "cannot interpret tstamp in who output\n%s" % (out)) + "cannot interpret tstamp in who output\n%s" % (out) + ) with self.subTest(psutil=psutil.users(), who=out): for idx, u in enumerate(psutil.users()): psutil_value = datetime.datetime.fromtimestamp( - u.started).strftime(tstamp) + u.started + ).strftime(tstamp) self.assertEqual(psutil_value, started[idx]) def test_pid_exists_let_raise(self): # According to "man 2 kill" possible error values for kill # are (EINVAL, EPERM, ESRCH). Test that any other errno # results in an exception. - with mock.patch("psutil._psposix.os.kill", - side_effect=OSError(errno.EBADF, "")) as m: + with mock.patch( + "psutil._psposix.os.kill", side_effect=OSError(errno.EBADF, "") + ) as m: self.assertRaises(OSError, psutil._psposix.pid_exists, os.getpid()) assert m.called def test_os_waitpid_let_raise(self): # os.waitpid() is supposed to catch EINTR and ECHILD only. # Test that any other errno results in an exception. - with mock.patch("psutil._psposix.os.waitpid", - side_effect=OSError(errno.EBADF, "")) as m: + with mock.patch( + "psutil._psposix.os.waitpid", side_effect=OSError(errno.EBADF, "") + ) as m: self.assertRaises(OSError, psutil._psposix.wait_pid, os.getpid()) assert m.called def test_os_waitpid_eintr(self): # os.waitpid() is supposed to "retry" on EINTR. - with mock.patch("psutil._psposix.os.waitpid", - side_effect=OSError(errno.EINTR, "")) as m: + with mock.patch( + "psutil._psposix.os.waitpid", side_effect=OSError(errno.EINTR, "") + ) as m: self.assertRaises( psutil._psposix.TimeoutExpired, - psutil._psposix.wait_pid, os.getpid(), timeout=0.01) + psutil._psposix.wait_pid, + os.getpid(), + timeout=0.01, + ) assert m.called def test_os_waitpid_bad_ret_status(self): # Simulate os.waitpid() returning a bad status. - with mock.patch("psutil._psposix.os.waitpid", - return_value=(1, -1)) as m: - self.assertRaises(ValueError, - psutil._psposix.wait_pid, os.getpid()) + with mock.patch( + "psutil._psposix.os.waitpid", return_value=(1, -1) + ) as m: + self.assertRaises( + ValueError, psutil._psposix.wait_pid, os.getpid() + ) assert m.called # AIX can return '-' in df output instead of numbers, e.g. for /proc @@ -446,9 +464,11 @@ def df(device): # https://travis-ci.org/giampaolo/psutil/jobs/138338464 # https://travis-ci.org/giampaolo/psutil/jobs/138343361 err = str(err).lower() - if "no such file or directory" in err or \ - "raw devices not supported" in err or \ - "permission denied" in err: + if ( + "no such file or directory" in err + or "raw devices not supported" in err + or "permission denied" in err + ): continue raise else: @@ -460,7 +480,6 @@ def df(device): @unittest.skipIf(not POSIX, "POSIX only") class TestMisc(PsutilTestCase): - def test_getpagesize(self): pagesize = getpagesize() self.assertGreater(pagesize, 0) @@ -470,4 +489,5 @@ def test_getpagesize(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index d7d06854c..5ccd43b3f 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -123,13 +123,15 @@ def test_send_signal(self): def test_send_signal_mocked(self): sig = signal.SIGTERM p = self.spawn_psproc() - with mock.patch('psutil.os.kill', - side_effect=OSError(errno.ESRCH, "")): + with mock.patch( + 'psutil.os.kill', side_effect=OSError(errno.ESRCH, "") + ): self.assertRaises(psutil.NoSuchProcess, p.send_signal, sig) p = self.spawn_psproc() - with mock.patch('psutil.os.kill', - side_effect=OSError(errno.EPERM, "")): + with mock.patch( + 'psutil.os.kill', side_effect=OSError(errno.EPERM, "") + ): self.assertRaises(psutil.AccessDenied, p.send_signal, sig) def test_wait_exited(self): @@ -248,8 +250,8 @@ def test_cpu_percent_numcpus_none(self): def test_cpu_times(self): times = psutil.Process().cpu_times() assert (times.user > 0.0) or (times.system > 0.0), times - assert (times.children_user >= 0.0), times - assert (times.children_system >= 0.0), times + assert times.children_user >= 0.0, times + assert times.children_system >= 0.0, times if LINUX: assert times.iowait >= 0.0, times # make sure returned values can be pretty printed with strftime @@ -288,8 +290,10 @@ def test_create_time(self): # It will fail if the difference between the values is > 2s. difference = abs(create_time - now) if difference > 2: - raise self.fail("expected: %s, found: %s, difference: %s" - % (now, create_time, difference)) + raise self.fail( + "expected: %s, found: %s, difference: %s" + % (now, create_time, difference) + ) # make sure returned value can be pretty printed with strftime time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time())) @@ -347,7 +351,6 @@ def test_io_counters(self): @unittest.skipIf(not HAS_IONICE, "not supported") @unittest.skipIf(not LINUX, "linux only") def test_ionice_linux(self): - def cleanup(init): ioclass, value = init if ioclass == psutil.IOPRIO_CLASS_NONE: @@ -386,7 +389,7 @@ def cleanup(init): with self.assertRaisesRegex(ValueError, "ioclass accepts no value"): p.ionice(psutil.IOPRIO_CLASS_IDLE, 1) with self.assertRaisesRegex( - ValueError, "'ioclass' argument must be specified", + ValueError, "'ioclass' argument must be specified" ): p.ionice(value=1) @@ -412,7 +415,7 @@ def test_ionice_win(self): self.assertEqual(p.ionice(), psutil.IOPRIO_HIGH) # errs with self.assertRaisesRegex( - TypeError, "value argument not accepted on Windows", + TypeError, "value argument not accepted on Windows" ): p.ionice(psutil.IOPRIO_NORMAL, value=1) with self.assertRaisesRegex(ValueError, "is not a valid priority"): @@ -421,6 +424,7 @@ def test_ionice_win(self): @unittest.skipIf(not HAS_RLIMIT, "not supported") def test_rlimit_get(self): import resource + p = psutil.Process(os.getpid()) names = [x for x in dir(psutil) if x.startswith('RLIMIT')] assert names, names @@ -468,8 +472,9 @@ def test_rlimit(self): with self.assertRaises(IOError) as exc: with open(testfn, "wb") as f: f.write(b"X" * 1025) - self.assertEqual(exc.exception.errno if PY3 else exc.exception[0], - errno.EFBIG) + self.assertEqual( + exc.exception.errno if PY3 else exc.exception[0], errno.EFBIG + ) finally: p.rlimit(psutil.RLIMIT_FSIZE, (soft, hard)) self.assertEqual(p.rlimit(psutil.RLIMIT_FSIZE), (soft, hard)) @@ -553,14 +558,17 @@ def test_threads_2(self): try: p.threads() except psutil.AccessDenied: - raise unittest.SkipTest( - "on OpenBSD this requires root access") + raise unittest.SkipTest("on OpenBSD this requires root access") self.assertAlmostEqual( p.cpu_times().user, - sum([x.user_time for x in p.threads()]), delta=0.1) + sum([x.user_time for x in p.threads()]), + delta=0.1, + ) self.assertAlmostEqual( p.cpu_times().system, - sum([x.system_time for x in p.threads()]), delta=0.1) + sum([x.system_time for x in p.threads()]), + delta=0.1, + ) @retry_on_failure() def test_memory_info(self): @@ -621,8 +629,9 @@ def test_memory_maps(self): assert os.path.isabs(nt.path), nt.path if POSIX: try: - assert os.path.exists(nt.path) or \ - os.path.islink(nt.path), nt.path + assert os.path.exists(nt.path) or os.path.islink( + nt.path + ), nt.path except AssertionError: if not LINUX: raise @@ -659,10 +668,11 @@ def test_memory_maps_lists_lib(self): # Make sure a newly loaded shared lib is listed. p = psutil.Process() with copyload_shared_lib() as path: + def normpath(p): return os.path.realpath(os.path.normcase(p)) - libpaths = [normpath(x.path) - for x in p.memory_maps()] + + libpaths = [normpath(x.path) for x in p.memory_maps()] self.assertIn(normpath(path), libpaths) def test_memory_percent(self): @@ -700,8 +710,9 @@ def test_exe(self): # an error. ver = "%s.%s" % (sys.version_info[0], sys.version_info[1]) try: - self.assertEqual(exe.replace(ver, ''), - PYTHON_EXE.replace(ver, '')) + self.assertEqual( + exe.replace(ver, ''), PYTHON_EXE.replace(ver, '') + ) except AssertionError: # Typically MACOS. Really not sure what to do here. pass @@ -723,8 +734,9 @@ def test_cmdline(self): if MACOS and CI_TESTING: pyexe = p.cmdline()[0] if pyexe != PYTHON_EXE: - self.assertEqual(' '.join(p.cmdline()[1:]), - ' '.join(cmdline[1:])) + self.assertEqual( + ' '.join(p.cmdline()[1:]), ' '.join(cmdline[1:]) + ) return self.assertEqual(' '.join(p.cmdline()), ' '.join(cmdline)) @@ -784,14 +796,21 @@ def test_prog_w_funky_name(self): # https://github.com/giampaolo/psutil/issues/628 funky_path = self.get_testfn(suffix='foo bar )') create_exe(funky_path) - cmdline = [funky_path, "-c", - "import time; [time.sleep(0.01) for x in range(3000)];" - "arg1", "arg2", "", "arg3", ""] + cmdline = [ + funky_path, + "-c", + "import time; [time.sleep(0.01) for x in range(3000)];arg1", + "arg2", + "", + "arg3", + "", + ] p = self.spawn_psproc(cmdline) self.assertEqual(p.cmdline(), cmdline) self.assertEqual(p.name(), os.path.basename(funky_path)) - self.assertEqual(os.path.normcase(p.exe()), - os.path.normcase(funky_path)) + self.assertEqual( + os.path.normcase(p.exe()), os.path.normcase(funky_path) + ) @unittest.skipIf(not POSIX, 'POSIX only') def test_uids(self): @@ -835,12 +854,14 @@ def cleanup(init): if WINDOWS: highest_prio = None - for prio in [psutil.IDLE_PRIORITY_CLASS, - psutil.BELOW_NORMAL_PRIORITY_CLASS, - psutil.NORMAL_PRIORITY_CLASS, - psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS]: + for prio in [ + psutil.IDLE_PRIORITY_CLASS, + psutil.BELOW_NORMAL_PRIORITY_CLASS, + psutil.NORMAL_PRIORITY_CLASS, + psutil.ABOVE_NORMAL_PRIORITY_CLASS, + psutil.HIGH_PRIORITY_CLASS, + psutil.REALTIME_PRIORITY_CLASS, + ]: with self.subTest(prio=prio): try: p.nice(prio) @@ -852,9 +873,11 @@ def cleanup(init): # even if the function succeeds. For higher # priorities, we match either the expected # value or the highest so far. - if prio in (psutil.ABOVE_NORMAL_PRIORITY_CLASS, - psutil.HIGH_PRIORITY_CLASS, - psutil.REALTIME_PRIORITY_CLASS): + if prio in ( + psutil.ABOVE_NORMAL_PRIORITY_CLASS, + psutil.HIGH_PRIORITY_CLASS, + psutil.REALTIME_PRIORITY_CLASS, + ): if new_prio == prio or highest_prio is None: highest_prio = prio self.assertEqual(new_prio, highest_prio) @@ -864,14 +887,14 @@ def cleanup(init): try: if hasattr(os, "getpriority"): self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), - p.nice()) + os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice() + ) p.nice(1) self.assertEqual(p.nice(), 1) if hasattr(os, "getpriority"): self.assertEqual( - os.getpriority(os.PRIO_PROCESS, os.getpid()), - p.nice()) + os.getpriority(os.PRIO_PROCESS, os.getpid()), p.nice() + ) # XXX - going back to previous nice value raises # AccessDenied on MACOS if not MACOS: @@ -906,8 +929,11 @@ def test_cwd(self): self.assertEqual(p.cwd(), os.getcwd()) def test_cwd_2(self): - cmd = [PYTHON_EXE, "-c", - "import os, time; os.chdir('..'); time.sleep(60)"] + cmd = [ + PYTHON_EXE, + "-c", + "import os, time; os.chdir('..'); time.sleep(60)", + ] p = self.spawn_psproc(cmd) call_until(p.cwd, "ret == os.path.dirname(os.getcwd())") @@ -927,8 +953,9 @@ def test_cpu_affinity(self): p.cpu_affinity([n]) self.assertEqual(p.cpu_affinity(), [n]) if hasattr(os, "sched_getaffinity"): - self.assertEqual(p.cpu_affinity(), - list(os.sched_getaffinity(p.pid))) + self.assertEqual( + p.cpu_affinity(), list(os.sched_getaffinity(p.pid)) + ) # also test num_cpu() if hasattr(p, "num_cpu"): self.assertEqual(p.cpu_affinity()[0], p.num_cpu()) @@ -942,8 +969,9 @@ def test_cpu_affinity(self): else: self.assertEqual(p.cpu_affinity(), all_cpus) if hasattr(os, "sched_getaffinity"): - self.assertEqual(p.cpu_affinity(), - list(os.sched_getaffinity(p.pid))) + self.assertEqual( + p.cpu_affinity(), list(os.sched_getaffinity(p.pid)) + ) # self.assertRaises(TypeError, p.cpu_affinity, 1) p.cpu_affinity(initial) @@ -1011,7 +1039,7 @@ def test_open_files(self): filenames = [os.path.normcase(x.path) for x in p.open_files()] if testfn in filenames: break - time.sleep(.01) + time.sleep(0.01) else: self.assertIn(os.path.normcase(testfn), filenames) for file in filenames: @@ -1028,12 +1056,15 @@ def test_open_files_2(self): testfn = self.get_testfn() with open(testfn, 'w') as fileobj: for file in p.open_files(): - if normcase(file.path) == normcase(fileobj.name) or \ - file.fd == fileobj.fileno(): + if ( + normcase(file.path) == normcase(fileobj.name) + or file.fd == fileobj.fileno() + ): break else: - raise self.fail("no file found; files=%s" % ( - repr(p.open_files()))) + raise self.fail( + "no file found; files=%s" % (repr(p.open_files())) + ) self.assertEqual(normcase(file.path), normcase(fileobj.name)) if WINDOWS: self.assertEqual(file.fd, -1) @@ -1188,27 +1219,36 @@ def test_as_dict(self): self.assertEqual(d['connections'], 'foo') # Test ad_value is set on AccessDenied. - with mock.patch('psutil.Process.nice', create=True, - side_effect=psutil.AccessDenied): + with mock.patch( + 'psutil.Process.nice', create=True, side_effect=psutil.AccessDenied + ): self.assertEqual( - p.as_dict(attrs=["nice"], ad_value=1), {"nice": 1}) + p.as_dict(attrs=["nice"], ad_value=1), {"nice": 1} + ) # Test that NoSuchProcess bubbles up. - with mock.patch('psutil.Process.nice', create=True, - side_effect=psutil.NoSuchProcess(p.pid, "name")): - self.assertRaises( - psutil.NoSuchProcess, p.as_dict, attrs=["nice"]) + with mock.patch( + 'psutil.Process.nice', + create=True, + side_effect=psutil.NoSuchProcess(p.pid, "name"), + ): + self.assertRaises(psutil.NoSuchProcess, p.as_dict, attrs=["nice"]) # Test that ZombieProcess is swallowed. - with mock.patch('psutil.Process.nice', create=True, - side_effect=psutil.ZombieProcess(p.pid, "name")): + with mock.patch( + 'psutil.Process.nice', + create=True, + side_effect=psutil.ZombieProcess(p.pid, "name"), + ): self.assertEqual( - p.as_dict(attrs=["nice"], ad_value="foo"), {"nice": "foo"}) + p.as_dict(attrs=["nice"], ad_value="foo"), {"nice": "foo"} + ) # By default APIs raising NotImplementedError are # supposed to be skipped. - with mock.patch('psutil.Process.nice', create=True, - side_effect=NotImplementedError): + with mock.patch( + 'psutil.Process.nice', create=True, side_effect=NotImplementedError + ): d = p.as_dict() self.assertNotIn('nice', list(d.keys())) # ...unless the user explicitly asked for some attr. @@ -1294,8 +1334,9 @@ def assert_raises_nsp(fun, fun_name): # NtQuerySystemInformation succeeds even if process is gone. if WINDOWS and fun_name in ('exe', 'name'): return - raise self.fail("%r didn't raise NSP and returned %r " - "instead" % (fun, ret)) + raise self.fail( + "%r didn't raise NSP and returned %r instead" % (fun, ret) + ) p = self.spawn_psproc() p.terminate() @@ -1318,8 +1359,9 @@ def test_zombie_process_is_running_w_exc(self): # Emulate a case where internally is_running() raises # ZombieProcess. p = psutil.Process() - with mock.patch("psutil.Process", - side_effect=psutil.ZombieProcess(0)) as m: + with mock.patch( + "psutil.Process", side_effect=psutil.ZombieProcess(0) + ) as m: assert p.is_running() assert m.called @@ -1328,8 +1370,10 @@ def test_zombie_process_status_w_exc(self): # Emulate a case where internally status() raises # ZombieProcess. p = psutil.Process() - with mock.patch("psutil._psplatform.Process.status", - side_effect=psutil.ZombieProcess(0)) as m: + with mock.patch( + "psutil._psplatform.Process.status", + side_effect=psutil.ZombieProcess(0), + ) as m: self.assertEqual(p.status(), psutil.STATUS_ZOMBIE) assert m.called @@ -1399,10 +1443,13 @@ def clean_dict(d): d.pop("__CF_USER_TEXT_ENCODING", None) d.pop("VERSIONER_PYTHON_PREFER_32_BIT", None) d.pop("VERSIONER_PYTHON_VERSION", None) - return dict( - [(k.replace("\r", "").replace("\n", ""), - v.replace("\r", "").replace("\n", "")) - for k, v in d.items()]) + return dict([ + ( + k.replace("\r", "").replace("\n", ""), + v.replace("\r", "").replace("\n", ""), + ) + for k, v in d.items() + ]) self.maxDiff = None p = psutil.Process() @@ -1437,7 +1484,8 @@ def test_weird_environ(self): path = self.get_testfn() create_exe(path, c_code=code) sproc = self.spawn_testproc( - [path], stdin=subprocess.PIPE, stderr=subprocess.PIPE) + [path], stdin=subprocess.PIPE, stderr=subprocess.PIPE + ) p = psutil.Process(sproc.pid) wait_for_pid(p.pid) assert p.is_running() @@ -1487,6 +1535,7 @@ def test_(self): meth() # noqa except psutil.AccessDenied: pass + setattr(self, attr, types.MethodType(test_, self)) def setUp(self): @@ -1529,8 +1578,12 @@ def test_misc(self): # psutil.__subproc instance doesn't get properly freed. # Not sure what to do though. cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"] - with psutil.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=PYTHON_EXE_ENV) as proc: + with psutil.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=PYTHON_EXE_ENV, + ) as proc: proc.name() proc.cpu_times() proc.stdin # noqa @@ -1543,10 +1596,13 @@ def test_misc(self): self.assertEqual(proc.wait(5), signal.SIGTERM) def test_ctx_manager(self): - with psutil.Popen([PYTHON_EXE, "-V"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=subprocess.PIPE, env=PYTHON_EXE_ENV) as proc: + with psutil.Popen( + [PYTHON_EXE, "-V"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + env=PYTHON_EXE_ENV, + ) as proc: proc.communicate() assert proc.stdout.closed assert proc.stderr.closed @@ -1558,21 +1614,31 @@ def test_kill_terminate(self): # not raise exception after the process is gone. psutil.Popen # diverges from that. cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"] - with psutil.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=PYTHON_EXE_ENV) as proc: + with psutil.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=PYTHON_EXE_ENV, + ) as proc: proc.terminate() proc.wait() self.assertRaises(psutil.NoSuchProcess, proc.terminate) self.assertRaises(psutil.NoSuchProcess, proc.kill) - self.assertRaises(psutil.NoSuchProcess, proc.send_signal, - signal.SIGTERM) + self.assertRaises( + psutil.NoSuchProcess, proc.send_signal, signal.SIGTERM + ) if WINDOWS: - self.assertRaises(psutil.NoSuchProcess, proc.send_signal, - signal.CTRL_C_EVENT) - self.assertRaises(psutil.NoSuchProcess, proc.send_signal, - signal.CTRL_BREAK_EVENT) + self.assertRaises( + psutil.NoSuchProcess, proc.send_signal, signal.CTRL_C_EVENT + ) + self.assertRaises( + psutil.NoSuchProcess, + proc.send_signal, + signal.CTRL_BREAK_EVENT, + ) if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_sunos.py b/psutil/tests/test_sunos.py index 274584d65..548352281 100755 --- a/psutil/tests/test_sunos.py +++ b/psutil/tests/test_sunos.py @@ -17,7 +17,6 @@ @unittest.skipIf(not SUNOS, "SUNOS only") class SunOSSpecificTestCase(PsutilTestCase): - def test_swap_memory(self): out = sh('env PATH=/usr/sbin:/sbin:%s swap -l' % os.environ['PATH']) lines = out.strip().split('\n')[1:] @@ -42,4 +41,5 @@ def test_cpu_count(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py index 6da77d58f..152e378fd 100755 --- a/psutil/tests/test_system.py +++ b/psutil/tests/test_system.py @@ -62,7 +62,6 @@ class TestProcessAPIs(PsutilTestCase): - def test_process_iter(self): self.assertIn(os.getpid(), [x.pid for x in psutil.process_iter()]) sproc = self.spawn_testproc() @@ -74,14 +73,18 @@ def test_process_iter(self): # assert there are no duplicates ls = [x for x in psutil.process_iter()] - self.assertEqual(sorted(ls, key=lambda x: x.pid), - sorted(set(ls), key=lambda x: x.pid)) + self.assertEqual( + sorted(ls, key=lambda x: x.pid), + sorted(set(ls), key=lambda x: x.pid), + ) - with mock.patch('psutil.Process', - side_effect=psutil.NoSuchProcess(os.getpid())): + with mock.patch( + 'psutil.Process', side_effect=psutil.NoSuchProcess(os.getpid()) + ): self.assertEqual(list(psutil.process_iter()), []) - with mock.patch('psutil.Process', - side_effect=psutil.AccessDenied(os.getpid())): + with mock.patch( + 'psutil.Process', side_effect=psutil.AccessDenied(os.getpid()) + ): with self.assertRaises(psutil.AccessDenied): list(psutil.process_iter()) @@ -90,23 +93,29 @@ def test_prcess_iter_w_attrs(self): self.assertEqual(list(p.info.keys()), ['pid']) with self.assertRaises(ValueError): list(psutil.process_iter(attrs=['foo'])) - with mock.patch("psutil._psplatform.Process.cpu_times", - side_effect=psutil.AccessDenied(0, "")) as m: + with mock.patch( + "psutil._psplatform.Process.cpu_times", + side_effect=psutil.AccessDenied(0, ""), + ) as m: for p in psutil.process_iter(attrs=["pid", "cpu_times"]): self.assertIsNone(p.info['cpu_times']) self.assertGreaterEqual(p.info['pid'], 0) assert m.called - with mock.patch("psutil._psplatform.Process.cpu_times", - side_effect=psutil.AccessDenied(0, "")) as m: + with mock.patch( + "psutil._psplatform.Process.cpu_times", + side_effect=psutil.AccessDenied(0, ""), + ) as m: flag = object() for p in psutil.process_iter( - attrs=["pid", "cpu_times"], ad_value=flag): + attrs=["pid", "cpu_times"], ad_value=flag + ): self.assertIs(p.info['cpu_times'], flag) self.assertGreaterEqual(p.info['pid'], 0) assert m.called - @unittest.skipIf(PYPY and WINDOWS, - "spawn_testproc() unreliable on PYPY + WINDOWS") + @unittest.skipIf( + PYPY and WINDOWS, "spawn_testproc() unreliable on PYPY + WINDOWS" + ) def test_wait_procs(self): def callback(p): pids.append(p.pid) @@ -130,8 +139,9 @@ def callback(p): @retry_on_failure(30) def test_1(procs, callback): - gone, alive = psutil.wait_procs(procs, timeout=0.03, - callback=callback) + gone, alive = psutil.wait_procs( + procs, timeout=0.03, callback=callback + ) self.assertEqual(len(gone), 1) self.assertEqual(len(alive), 2) return gone, alive @@ -149,8 +159,9 @@ def test_1(procs, callback): @retry_on_failure(30) def test_2(procs, callback): - gone, alive = psutil.wait_procs(procs, timeout=0.03, - callback=callback) + gone, alive = psutil.wait_procs( + procs, timeout=0.03, callback=callback + ) self.assertEqual(len(gone), 3) self.assertEqual(len(alive), 0) return gone, alive @@ -162,8 +173,9 @@ def test_2(procs, callback): for p in gone: self.assertTrue(hasattr(p, 'returncode')) - @unittest.skipIf(PYPY and WINDOWS, - "spawn_testproc() unreliable on PYPY + WINDOWS") + @unittest.skipIf( + PYPY and WINDOWS, "spawn_testproc() unreliable on PYPY + WINDOWS" + ) def test_wait_procs_no_timeout(self): sproc1 = self.spawn_testproc() sproc2 = self.spawn_testproc() @@ -191,7 +203,7 @@ def test_pid_exists_2(self): except AssertionError: # in case the process disappeared in meantime fail only # if it is no longer in psutil.pids() - time.sleep(.1) + time.sleep(0.1) self.assertNotIn(pid, psutil.pids()) pids = range(max(pids) + 15000, max(pids) + 16000) for pid in pids: @@ -199,7 +211,6 @@ def test_pid_exists_2(self): class TestMiscAPIs(PsutilTestCase): - def test_boot_time(self): bt = psutil.boot_time() self.assertIsInstance(bt, float) @@ -236,8 +247,17 @@ def test_test(self): sys.stdout = stdout def test_os_constants(self): - names = ["POSIX", "WINDOWS", "LINUX", "MACOS", "FREEBSD", "OPENBSD", - "NETBSD", "BSD", "SUNOS"] + names = [ + "POSIX", + "WINDOWS", + "LINUX", + "MACOS", + "FREEBSD", + "OPENBSD", + "NETBSD", + "BSD", + "SUNOS", + ] for name in names: self.assertIsInstance(getattr(psutil, name), bool, msg=name) @@ -250,14 +270,20 @@ def test_os_constants(self): names.remove("LINUX") elif "bsd" in sys.platform.lower(): assert psutil.BSD - self.assertEqual([psutil.FREEBSD, psutil.OPENBSD, - psutil.NETBSD].count(True), 1) + self.assertEqual( + [psutil.FREEBSD, psutil.OPENBSD, psutil.NETBSD].count( + True + ), + 1, + ) names.remove("BSD") names.remove("FREEBSD") names.remove("OPENBSD") names.remove("NETBSD") - elif "sunos" in sys.platform.lower() or \ - "solaris" in sys.platform.lower(): + elif ( + "sunos" in sys.platform.lower() + or "solaris" in sys.platform.lower() + ): assert psutil.SUNOS names.remove("SUNOS") elif "darwin" in sys.platform.lower(): @@ -274,7 +300,6 @@ def test_os_constants(self): class TestMemoryAPIs(PsutilTestCase): - def test_virtual_memory(self): mem = psutil.virtual_memory() assert mem.total > 0, mem @@ -290,13 +315,16 @@ def test_virtual_memory(self): if not value >= 0: raise self.fail("%r < 0 (%s)" % (name, value)) if value > mem.total: - raise self.fail("%r > total (total=%s, %s=%s)" - % (name, mem.total, name, value)) + raise self.fail( + "%r > total (total=%s, %s=%s)" + % (name, mem.total, name, value) + ) def test_swap_memory(self): mem = psutil.swap_memory() self.assertEqual( - mem._fields, ('total', 'used', 'free', 'percent', 'sin', 'sout')) + mem._fields, ('total', 'used', 'free', 'percent', 'sin', 'sout') + ) assert mem.total >= 0, mem assert mem.used >= 0, mem @@ -311,7 +339,6 @@ def test_swap_memory(self): class TestCpuAPIs(PsutilTestCase): - def test_cpu_count_logical(self): logical = psutil.cpu_count() self.assertIsNotNone(logical) @@ -338,12 +365,14 @@ def test_cpu_count_cores(self): def test_cpu_count_none(self): # https://github.com/giampaolo/psutil/issues/1085 for val in (-1, 0, None): - with mock.patch('psutil._psplatform.cpu_count_logical', - return_value=val) as m: + with mock.patch( + 'psutil._psplatform.cpu_count_logical', return_value=val + ) as m: self.assertIsNone(psutil.cpu_count()) assert m.called - with mock.patch('psutil._psplatform.cpu_count_cores', - return_value=val) as m: + with mock.patch( + 'psutil._psplatform.cpu_count_cores', return_value=val + ) as m: self.assertIsNone(psutil.cpu_count(logical=False)) assert m.called @@ -397,8 +426,10 @@ def test_per_cpu_times(self): total += cp_time self.assertAlmostEqual(total, sum(times)) str(times) - self.assertEqual(len(psutil.cpu_times(percpu=True)[0]), - len(psutil.cpu_times(percpu=False))) + self.assertEqual( + len(psutil.cpu_times(percpu=True)[0]), + len(psutil.cpu_times(percpu=False)), + ) # Note: in theory CPU times are always supposed to increase over # time or remain the same but never go backwards. In practice @@ -447,7 +478,8 @@ def test_cpu_times_comparison(self): self.assertAlmostEqual( getattr(base, field), getattr(summed_values, field), - delta=1) + delta=1, + ) def _test_cpu_percent(self, percent, last_ret, new_ret): try: @@ -456,8 +488,10 @@ def _test_cpu_percent(self, percent, last_ret, new_ret): self.assertIsNot(percent, -0.0) self.assertLessEqual(percent, 100.0 * psutil.cpu_count()) except AssertionError as err: - raise AssertionError("\n%s\nlast=%s\nnew=%s" % ( - err, pprint.pformat(last_ret), pprint.pformat(new_ret))) + raise AssertionError( + "\n%s\nlast=%s\nnew=%s" + % (err, pprint.pformat(last_ret), pprint.pformat(new_ret)) + ) def test_cpu_percent(self): last = psutil.cpu_percent(interval=0.001) @@ -504,8 +538,10 @@ def test_per_cpu_times_percent(self): def test_per_cpu_times_percent_negative(self): # see: https://github.com/giampaolo/psutil/issues/645 psutil.cpu_times_percent(percpu=True) - zero_times = [x._make([0 for x in range(len(x._fields))]) - for x in psutil.cpu_times(percpu=True)] + zero_times = [ + x._make([0 for x in range(len(x._fields))]) + for x in psutil.cpu_times(percpu=True) + ] with mock.patch('psutil.cpu_times', return_value=zero_times): for cpu in psutil.cpu_times_percent(percpu=True): for percent in cpu: @@ -516,7 +552,8 @@ def test_cpu_stats(self): infos = psutil.cpu_stats() self.assertEqual( infos._fields, - ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls')) + ('ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'), + ) for name in infos._fields: value = getattr(infos, name) self.assertGreaterEqual(value, 0) @@ -525,8 +562,9 @@ def test_cpu_stats(self): self.assertGreater(value, 0) # TODO: remove this once 1892 is fixed - @unittest.skipIf(MACOS and platform.machine() == 'arm64', - "skipped due to #1892") + @unittest.skipIf( + MACOS and platform.machine() == 'arm64', "skipped due to #1892" + ) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_cpu_freq(self): def check_ls(ls): @@ -559,7 +597,6 @@ def test_getloadavg(self): class TestDiskAPIs(PsutilTestCase): - @unittest.skipIf(PYPY and not IS_64BIT, "unreliable on PYPY32 + 32BIT") def test_disk_usage(self): usage = psutil.disk_usage(os.getcwd()) @@ -576,12 +613,14 @@ def test_disk_usage(self): shutil_usage = shutil.disk_usage(os.getcwd()) tolerance = 5 * 1024 * 1024 # 5MB self.assertEqual(usage.total, shutil_usage.total) - self.assertAlmostEqual(usage.free, shutil_usage.free, - delta=tolerance) + self.assertAlmostEqual( + usage.free, shutil_usage.free, delta=tolerance + ) if not MACOS_12PLUS: # see https://github.com/giampaolo/psutil/issues/2147 - self.assertAlmostEqual(usage.used, shutil_usage.used, - delta=tolerance) + self.assertAlmostEqual( + usage.used, shutil_usage.used, delta=tolerance + ) # if path does not exist OSError ENOENT is expected across # all platforms @@ -655,14 +694,20 @@ def find_mount_point(path): return path.lower() mount = find_mount_point(__file__) - mounts = [x.mountpoint.lower() for x in - psutil.disk_partitions(all=True) if x.mountpoint] + mounts = [ + x.mountpoint.lower() + for x in psutil.disk_partitions(all=True) + if x.mountpoint + ] self.assertIn(mount, mounts) - @unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'), - '/proc/diskstats not available on this linux version') - @unittest.skipIf(CI_TESTING and not psutil.disk_io_counters(), - "unreliable on CI") # no visible disks + @unittest.skipIf( + LINUX and not os.path.exists('/proc/diskstats'), + '/proc/diskstats not available on this linux version', + ) + @unittest.skipIf( + CI_TESTING and not psutil.disk_io_counters(), "unreliable on CI" + ) # no visible disks def test_disk_io_counters(self): def check_ntuple(nt): self.assertEqual(nt[0], nt.read_count) @@ -694,15 +739,15 @@ def check_ntuple(nt): def test_disk_io_counters_no_disks(self): # Emulate a case where no disks are installed, see: # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch('psutil._psplatform.disk_io_counters', - return_value={}) as m: + with mock.patch( + 'psutil._psplatform.disk_io_counters', return_value={} + ) as m: self.assertIsNone(psutil.disk_io_counters(perdisk=False)) self.assertEqual(psutil.disk_io_counters(perdisk=True), {}) assert m.called class TestNetAPIs(PsutilTestCase): - @unittest.skipIf(not HAS_NET_IO_COUNTERS, 'not supported') def test_net_io_counters(self): def check_ntuple(nt): @@ -736,8 +781,9 @@ def check_ntuple(nt): def test_net_io_counters_no_nics(self): # Emulate a case where no NICs are installed, see: # https://github.com/giampaolo/psutil/issues/1062 - with mock.patch('psutil._psplatform.net_io_counters', - return_value={}) as m: + with mock.patch( + 'psutil._psplatform.net_io_counters', return_value={} + ) as m: self.assertIsNone(psutil.net_io_counters(pernic=False)) self.assertEqual(psutil.net_io_counters(pernic=True), {}) assert m.called @@ -774,14 +820,23 @@ def test_net_if_addrs(self): s.bind((addr.address, 0)) elif addr.family == socket.AF_INET6: info = socket.getaddrinfo( - addr.address, 0, socket.AF_INET6, - socket.SOCK_STREAM, 0, socket.AI_PASSIVE)[0] + addr.address, + 0, + socket.AF_INET6, + socket.SOCK_STREAM, + 0, + socket.AI_PASSIVE, + )[0] af, socktype, proto, canonname, sa = info s = socket.socket(af, socktype, proto) with contextlib.closing(s): s.bind(sa) - for ip in (addr.address, addr.netmask, addr.broadcast, - addr.ptp): + for ip in ( + addr.address, + addr.netmask, + addr.broadcast, + addr.ptp, + ): if ip is not None: # TODO: skip AF_INET6 for now because I get: # AddressValueError: Only hex digits permitted in @@ -810,8 +865,9 @@ def test_net_if_addrs_mac_null_bytes(self): ret = [('em1', psutil.AF_LINK, '06:3d:29', None, None, None)] else: ret = [('em1', -1, '06-3d-29', None, None, None)] - with mock.patch('psutil._psplatform.net_if_addrs', - return_value=ret) as m: + with mock.patch( + 'psutil._psplatform.net_if_addrs', return_value=ret + ) as m: addr = psutil.net_if_addrs()['em1'][0] assert m.called if POSIX: @@ -822,9 +878,11 @@ def test_net_if_addrs_mac_null_bytes(self): def test_net_if_stats(self): nics = psutil.net_if_stats() assert nics, nics - all_duplexes = (psutil.NIC_DUPLEX_FULL, - psutil.NIC_DUPLEX_HALF, - psutil.NIC_DUPLEX_UNKNOWN) + all_duplexes = ( + psutil.NIC_DUPLEX_FULL, + psutil.NIC_DUPLEX_HALF, + psutil.NIC_DUPLEX_UNKNOWN, + ) for name, stats in nics.items(): self.assertIsInstance(name, str) isup, duplex, speed, mtu, flags = stats @@ -835,19 +893,21 @@ def test_net_if_stats(self): self.assertGreaterEqual(mtu, 0) self.assertIsInstance(flags, str) - @unittest.skipIf(not (LINUX or BSD or MACOS), - "LINUX or BSD or MACOS specific") + @unittest.skipIf( + not (LINUX or BSD or MACOS), "LINUX or BSD or MACOS specific" + ) def test_net_if_stats_enodev(self): # See: https://github.com/giampaolo/psutil/issues/1279 - with mock.patch('psutil._psutil_posix.net_if_mtu', - side_effect=OSError(errno.ENODEV, "")) as m: + with mock.patch( + 'psutil._psutil_posix.net_if_mtu', + side_effect=OSError(errno.ENODEV, ""), + ) as m: ret = psutil.net_if_stats() self.assertEqual(ret, {}) assert m.called class TestSensorsAPIs(PsutilTestCase): - @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures(self): temps = psutil.sensors_temperatures() @@ -865,10 +925,10 @@ def test_sensors_temperatures(self): @unittest.skipIf(not HAS_SENSORS_TEMPERATURES, "not supported") def test_sensors_temperatures_fahreneit(self): d = {'coretemp': [('label', 50.0, 60.0, 70.0)]} - with mock.patch("psutil._psplatform.sensors_temperatures", - return_value=d) as m: - temps = psutil.sensors_temperatures( - fahrenheit=True)['coretemp'][0] + with mock.patch( + "psutil._psplatform.sensors_temperatures", return_value=d + ) as m: + temps = psutil.sensors_temperatures(fahrenheit=True)['coretemp'][0] assert m.called self.assertEqual(temps.current, 122.0) self.assertEqual(temps.high, 140.0) @@ -880,8 +940,10 @@ def test_sensors_battery(self): ret = psutil.sensors_battery() self.assertGreaterEqual(ret.percent, 0) self.assertLessEqual(ret.percent, 100) - if ret.secsleft not in (psutil.POWER_TIME_UNKNOWN, - psutil.POWER_TIME_UNLIMITED): + if ret.secsleft not in ( + psutil.POWER_TIME_UNKNOWN, + psutil.POWER_TIME_UNLIMITED, + ): self.assertGreaterEqual(ret.secsleft, 0) else: if ret.secsleft == psutil.POWER_TIME_UNLIMITED: @@ -901,4 +963,5 @@ def test_sensors_fans(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py index bff43b1c9..825311064 100755 --- a/psutil/tests/test_testutils.py +++ b/psutil/tests/test_testutils.py @@ -60,7 +60,6 @@ class TestRetryDecorator(PsutilTestCase): - @mock.patch('time.sleep') def test_retry_success(self, sleep): # Fail 3 times out of 5; make sure the decorated fun returns. @@ -112,7 +111,6 @@ def foo(): @mock.patch('time.sleep') def test_retries_arg(self, sleep): - @retry(retries=5, interval=1, logfun=None) def foo(): 1 / 0 # noqa @@ -126,7 +124,6 @@ def test_retries_and_timeout_args(self, sleep): class TestSyncTestUtils(PsutilTestCase): - def test_wait_for_pid(self): wait_for_pid(os.getpid()) nopid = max(psutil.pids()) + 99999 @@ -165,7 +162,6 @@ def test_call_until(self): class TestFSTestUtils(PsutilTestCase): - def test_open_text(self): with open_text(__file__) as f: self.assertEqual(f.mode, 'r') @@ -194,8 +190,9 @@ def test_safe_rmpath(self): safe_rmpath(testfn) assert not os.path.exists(testfn) # test other exceptions are raised - with mock.patch('psutil.tests.os.stat', - side_effect=OSError(errno.EINVAL, "")) as m: + with mock.patch( + 'psutil.tests.os.stat', side_effect=OSError(errno.EINVAL, "") + ) as m: with self.assertRaises(OSError): safe_rmpath(testfn) assert m.called @@ -210,7 +207,6 @@ def test_chdir(self): class TestProcessUtils(PsutilTestCase): - def test_reap_children(self): subp = self.spawn_testproc() p = psutil.Process(subp.pid) @@ -259,8 +255,12 @@ def test_terminate(self): terminate(p) # by psutil.Popen cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"] - p = psutil.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=PYTHON_EXE_ENV) + p = psutil.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=PYTHON_EXE_ENV, + ) terminate(p) self.assertPidGone(p.pid) terminate(p) @@ -279,7 +279,6 @@ def test_terminate(self): class TestNetUtils(PsutilTestCase): - def bind_socket(self): port = get_free_port() with contextlib.closing(bind_socket(addr=('', port))) as s: @@ -313,8 +312,9 @@ def tcp_tcp_socketpair(self): self.assertNotEqual(client.getsockname(), addr) @unittest.skipIf(not POSIX, "POSIX only") - @unittest.skipIf(NETBSD or FREEBSD, - "/var/run/log UNIX socket opened by default") + @unittest.skipIf( + NETBSD or FREEBSD, "/var/run/log UNIX socket opened by default" + ) def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() @@ -351,11 +351,11 @@ def test_create_sockets(self): @serialrun class TestMemLeakClass(TestMemoryLeak): - @retry_on_failure() def test_times(self): def fun(): cnt['cnt'] += 1 + cnt = {'cnt': 0} self.execute(fun, times=10, warmup_times=15) self.assertEqual(cnt['cnt'], 26) @@ -378,8 +378,9 @@ def fun(ls=ls): try: # will consume around 3M in total - self.assertRaisesRegex(AssertionError, "extra-mem", - self.execute, fun, times=50) + self.assertRaisesRegex( + AssertionError, "extra-mem", self.execute, fun, times=50 + ) finally: del ls @@ -391,33 +392,37 @@ def fun(): box = [] kind = "fd" if POSIX else "handle" - self.assertRaisesRegex(AssertionError, "unclosed " + kind, - self.execute, fun) + self.assertRaisesRegex( + AssertionError, "unclosed " + kind, self.execute, fun + ) def test_tolerance(self): def fun(): ls.append("x" * 24 * 1024) + ls = [] times = 100 - self.execute(fun, times=times, warmup_times=0, - tolerance=200 * 1024 * 1024) + self.execute( + fun, times=times, warmup_times=0, tolerance=200 * 1024 * 1024 + ) self.assertEqual(len(ls), times + 1) def test_execute_w_exc(self): def fun_1(): 1 / 0 # noqa + self.execute_w_exc(ZeroDivisionError, fun_1) with self.assertRaises(ZeroDivisionError): self.execute_w_exc(OSError, fun_1) def fun_2(): pass + with self.assertRaises(AssertionError): self.execute_w_exc(ZeroDivisionError, fun_2) class TestTestingUtils(PsutilTestCase): - def test_process_namespace(self): p = psutil.Process() ns = process_namespace(p) @@ -432,7 +437,6 @@ def test_system_namespace(self): class TestOtherUtils(PsutilTestCase): - def test_is_namedtuple(self): assert is_namedtuple(collections.namedtuple('foo', 'a b c')(1, 2, 3)) assert not is_namedtuple(tuple()) @@ -440,4 +444,5 @@ def test_is_namedtuple(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index cfc451219..3ab71b03a 100755 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -111,6 +111,7 @@ if APPVEYOR: + def safe_rmpath(path): # NOQA # TODO - this is quite random and I'm not sure why it happens, # nor I can reproduce it locally: @@ -123,6 +124,7 @@ def safe_rmpath(path): # NOQA # 68c7a70728a31d8b8b58f4be6c4c0baa2f449eda/psutil/arch/ # windows/process_info.c#L146 from psutil.tests import safe_rmpath as rm + try: return rm(path) except WindowsError: @@ -209,8 +211,9 @@ def test_proc_exe(self): exe = p.exe() self.assertIsInstance(exe, str) if self.expect_exact_path_match(): - self.assertEqual(os.path.normcase(exe), - os.path.normcase(self.funky_name)) + self.assertEqual( + os.path.normcase(exe), os.path.normcase(self.funky_name) + ) def test_proc_name(self): cmd = [self.funky_name, "-c", "time.sleep(10)"] @@ -229,7 +232,8 @@ def test_proc_cmdline(self): self.assertIsInstance(part, str) if self.expect_exact_path_match(): self.assertEqual( - cmdline, [self.funky_name, "-c", "time.sleep(10)"]) + cmdline, [self.funky_name, "-c", "time.sleep(10)"] + ) def test_proc_cwd(self): dname = self.funky_name + "2" @@ -254,8 +258,9 @@ def test_proc_open_files(self): # XXX - see https://github.com/giampaolo/psutil/issues/595 return self.skipTest("open_files on BSD is broken") if self.expect_exact_path_match(): - self.assertEqual(os.path.normcase(path), - os.path.normcase(self.funky_name)) + self.assertEqual( + os.path.normcase(path), os.path.normcase(self.funky_name) + ) @unittest.skipIf(not POSIX, "POSIX only") def test_proc_connections(self): @@ -309,10 +314,13 @@ def test_memory_maps(self): # XXX: on Python 2, using ctypes.CDLL with a unicode path # opens a message box which blocks the test run. with copyload_shared_lib(suffix=self.funky_suffix) as funky_path: + def normpath(p): return os.path.realpath(os.path.normcase(p)) - libpaths = [normpath(x.path) - for x in psutil.Process().memory_maps()] + + libpaths = [ + normpath(x.path) for x in psutil.Process().memory_maps() + ] # ...just to have a clearer msg in case of failure libpaths = [x for x in libpaths if TESTFN_PREFIX in x] self.assertIn(normpath(funky_path), libpaths) @@ -362,4 +370,5 @@ def test_proc_environ(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/psutil/tests/test_windows.py b/psutil/tests/test_windows.py index dfc8444bd..5983af70a 100755 --- a/psutil/tests/test_windows.py +++ b/psutil/tests/test_windows.py @@ -72,9 +72,10 @@ def powershell(cmd): """ if not which("powershell.exe"): raise unittest.SkipTest("powershell.exe not available") - cmdline = \ - 'powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive ' + \ - '-NoProfile -WindowStyle Hidden -Command "%s"' % cmd + cmdline = ( + 'powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive ' + + '-NoProfile -WindowStyle Hidden -Command "%s"' % cmd + ) return sh(cmdline) @@ -101,9 +102,10 @@ def wmic(path, what, converter=int): class TestCpuAPIs(WindowsTestCase): - - @unittest.skipIf('NUMBER_OF_PROCESSORS' not in os.environ, - 'NUMBER_OF_PROCESSORS env var is not available') + @unittest.skipIf( + 'NUMBER_OF_PROCESSORS' not in os.environ, + 'NUMBER_OF_PROCESSORS env var is not available', + ) def test_cpu_count_vs_NUMBER_OF_PROCESSORS(self): # Will likely fail on many-cores systems: # https://stackoverflow.com/questions/31209256 @@ -119,8 +121,9 @@ def test_cpu_count_vs_GetSystemInfo(self): def test_cpu_count_logical_vs_wmi(self): w = wmi.WMI() - procs = sum(proc.NumberOfLogicalProcessors - for proc in w.Win32_Processor()) + procs = sum( + proc.NumberOfLogicalProcessors for proc in w.Win32_Processor() + ) self.assertEqual(psutil.cpu_count(), procs) def test_cpu_count_cores_vs_wmi(self): @@ -129,8 +132,9 @@ def test_cpu_count_cores_vs_wmi(self): self.assertEqual(psutil.cpu_count(logical=False), cores) def test_cpu_count_vs_cpu_times(self): - self.assertEqual(psutil.cpu_count(), - len(psutil.cpu_times(percpu=True))) + self.assertEqual( + psutil.cpu_count(), len(psutil.cpu_times(percpu=True)) + ) def test_cpu_freq(self): w = wmi.WMI() @@ -140,7 +144,6 @@ def test_cpu_freq(self): class TestSystemAPIs(WindowsTestCase): - def test_nic_names(self): out = sh('ipconfig /all') nics = psutil.net_io_counters(pernic=True).keys() @@ -149,38 +152,44 @@ def test_nic_names(self): continue if nic not in out: raise self.fail( - "%r nic wasn't found in 'ipconfig /all' output" % nic) + "%r nic wasn't found in 'ipconfig /all' output" % nic + ) def test_total_phymem(self): w = wmi.WMI().Win32_ComputerSystem()[0] - self.assertEqual(int(w.TotalPhysicalMemory), - psutil.virtual_memory().total) + self.assertEqual( + int(w.TotalPhysicalMemory), psutil.virtual_memory().total + ) def test_free_phymem(self): w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] self.assertAlmostEqual( - int(w.AvailableBytes), psutil.virtual_memory().free, - delta=TOLERANCE_SYS_MEM) + int(w.AvailableBytes), + psutil.virtual_memory().free, + delta=TOLERANCE_SYS_MEM, + ) def test_total_swapmem(self): w = wmi.WMI().Win32_PerfRawData_PerfOS_Memory()[0] - self.assertEqual(int(w.CommitLimit) - psutil.virtual_memory().total, - psutil.swap_memory().total) - if (psutil.swap_memory().total == 0): + self.assertEqual( + int(w.CommitLimit) - psutil.virtual_memory().total, + psutil.swap_memory().total, + ) + if psutil.swap_memory().total == 0: self.assertEqual(0, psutil.swap_memory().free) self.assertEqual(0, psutil.swap_memory().used) def test_percent_swapmem(self): - if (psutil.swap_memory().total > 0): - w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile( - Name="_Total")[0] + if psutil.swap_memory().total > 0: + w = wmi.WMI().Win32_PerfRawData_PerfOS_PagingFile(Name="_Total")[0] # calculate swap usage to percent percentSwap = int(w.PercentUsage) * 100 / int(w.PercentUsage_Base) # exact percent may change but should be reasonable # assert within +/- 5% and between 0 and 100% self.assertGreaterEqual(psutil.swap_memory().percent, 0) - self.assertAlmostEqual(psutil.swap_memory().percent, percentSwap, - delta=5) + self.assertAlmostEqual( + psutil.swap_memory().percent, percentSwap, delta=5 + ) self.assertLessEqual(psutil.swap_memory().percent, 100) # @unittest.skipIf(wmi is None, "wmi module is not installed") @@ -229,8 +238,9 @@ def test_disks(self): self.assertEqual(usage.free, wmi_free) # 10 MB tolerance if abs(usage.free - wmi_free) > 10 * 1024 * 1024: - raise self.fail("psutil=%s, wmi=%s" % ( - usage.free, wmi_free)) + raise self.fail( + "psutil=%s, wmi=%s" % (usage.free, wmi_free) + ) break else: raise self.fail("can't find partition %s" % repr(ps_part)) @@ -242,19 +252,27 @@ def test_disk_usage(self): continue sys_value = win32api.GetDiskFreeSpaceEx(disk.mountpoint) psutil_value = psutil.disk_usage(disk.mountpoint) - self.assertAlmostEqual(sys_value[0], psutil_value.free, - delta=TOLERANCE_DISK_USAGE) - self.assertAlmostEqual(sys_value[1], psutil_value.total, - delta=TOLERANCE_DISK_USAGE) - self.assertEqual(psutil_value.used, - psutil_value.total - psutil_value.free) + self.assertAlmostEqual( + sys_value[0], psutil_value.free, delta=TOLERANCE_DISK_USAGE + ) + self.assertAlmostEqual( + sys_value[1], psutil_value.total, delta=TOLERANCE_DISK_USAGE + ) + self.assertEqual( + psutil_value.used, psutil_value.total - psutil_value.free + ) def test_disk_partitions(self): sys_value = [ - x + '\\' for x in win32api.GetLogicalDriveStrings().split("\\\x00") - if x and not x.startswith('A:')] - psutil_value = [x.mountpoint for x in psutil.disk_partitions(all=True) - if not x.mountpoint.startswith('A:')] + x + '\\' + for x in win32api.GetLogicalDriveStrings().split("\\\x00") + if x and not x.startswith('A:') + ] + psutil_value = [ + x.mountpoint + for x in psutil.disk_partitions(all=True) + if not x.mountpoint.startswith('A:') + ] self.assertEqual(sys_value, psutil_value) def test_net_if_stats(self): @@ -264,14 +282,17 @@ def test_net_if_stats(self): for wmi_adapter in wmi_adapters: wmi_names.add(wmi_adapter.Name) wmi_names.add(wmi_adapter.NetConnectionID) - self.assertTrue(ps_names & wmi_names, - "no common entries in %s, %s" % (ps_names, wmi_names)) + self.assertTrue( + ps_names & wmi_names, + "no common entries in %s, %s" % (ps_names, wmi_names), + ) def test_boot_time(self): wmi_os = wmi.WMI().Win32_OperatingSystem() wmi_btime_str = wmi_os[0].LastBootUpTime.split('.')[0] wmi_btime_dt = datetime.datetime.strptime( - wmi_btime_str, "%Y%m%d%H%M%S") + wmi_btime_str, "%Y%m%d%H%M%S" + ) psutil_dt = datetime.datetime.fromtimestamp(psutil.boot_time()) diff = abs((wmi_btime_dt - psutil_dt).total_seconds()) self.assertLessEqual(diff, 5) @@ -294,7 +315,6 @@ def test_boot_time_fluctuation(self): class TestSensorsBattery(WindowsTestCase): - def test_has_battery(self): if win32api.GetPwrCapabilities()['SystemBatteriesPresent']: self.assertIsNotNone(psutil.sensors_battery()) @@ -307,8 +327,10 @@ def test_percent(self): battery_wmi = w.query('select * from Win32_Battery')[0] battery_psutil = psutil.sensors_battery() self.assertAlmostEqual( - battery_psutil.percent, battery_wmi.EstimatedChargeRemaining, - delta=1) + battery_psutil.percent, + battery_wmi.EstimatedChargeRemaining, + delta=1, + ) @unittest.skipIf(not HAS_BATTERY, "no battery") def test_power_plugged(self): @@ -317,34 +339,44 @@ def test_power_plugged(self): battery_psutil = psutil.sensors_battery() # Status codes: # https://msdn.microsoft.com/en-us/library/aa394074(v=vs.85).aspx - self.assertEqual(battery_psutil.power_plugged, - battery_wmi.BatteryStatus == 2) + self.assertEqual( + battery_psutil.power_plugged, battery_wmi.BatteryStatus == 2 + ) def test_emulate_no_battery(self): - with mock.patch("psutil._pswindows.cext.sensors_battery", - return_value=(0, 128, 0, 0)) as m: + with mock.patch( + "psutil._pswindows.cext.sensors_battery", + return_value=(0, 128, 0, 0), + ) as m: self.assertIsNone(psutil.sensors_battery()) assert m.called def test_emulate_power_connected(self): - with mock.patch("psutil._pswindows.cext.sensors_battery", - return_value=(1, 0, 0, 0)) as m: - self.assertEqual(psutil.sensors_battery().secsleft, - psutil.POWER_TIME_UNLIMITED) + with mock.patch( + "psutil._pswindows.cext.sensors_battery", return_value=(1, 0, 0, 0) + ) as m: + self.assertEqual( + psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + ) assert m.called def test_emulate_power_charging(self): - with mock.patch("psutil._pswindows.cext.sensors_battery", - return_value=(0, 8, 0, 0)) as m: - self.assertEqual(psutil.sensors_battery().secsleft, - psutil.POWER_TIME_UNLIMITED) + with mock.patch( + "psutil._pswindows.cext.sensors_battery", return_value=(0, 8, 0, 0) + ) as m: + self.assertEqual( + psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNLIMITED + ) assert m.called def test_emulate_secs_left_unknown(self): - with mock.patch("psutil._pswindows.cext.sensors_battery", - return_value=(0, 0, 0, -1)) as m: - self.assertEqual(psutil.sensors_battery().secsleft, - psutil.POWER_TIME_UNKNOWN) + with mock.patch( + "psutil._pswindows.cext.sensors_battery", + return_value=(0, 0, 0, -1), + ) as m: + self.assertEqual( + psutil.sensors_battery().secsleft, psutil.POWER_TIME_UNKNOWN + ) assert m.called @@ -354,7 +386,6 @@ def test_emulate_secs_left_unknown(self): class TestProcess(WindowsTestCase): - @classmethod def setUpClass(cls): cls.pid = spawn_testproc().pid @@ -391,8 +422,9 @@ def test_send_signal(self): def test_num_handles_increment(self): p = psutil.Process(os.getpid()) before = p.num_handles() - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, os.getpid()) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() + ) after = p.num_handles() self.assertEqual(after, before + 1) win32api.CloseHandle(handle) @@ -404,10 +436,12 @@ def test_ctrl_signals(self): p.send_signal(signal.CTRL_BREAK_EVENT) p.kill() p.wait() - self.assertRaises(psutil.NoSuchProcess, - p.send_signal, signal.CTRL_C_EVENT) - self.assertRaises(psutil.NoSuchProcess, - p.send_signal, signal.CTRL_BREAK_EVENT) + self.assertRaises( + psutil.NoSuchProcess, p.send_signal, signal.CTRL_C_EVENT + ) + self.assertRaises( + psutil.NoSuchProcess, p.send_signal, signal.CTRL_BREAK_EVENT + ) def test_username(self): name = win32api.GetUserNameEx(win32con.NameSamCompatible) @@ -445,43 +479,50 @@ def test_cmdline(self): # delta=0.2) def test_nice(self): - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, os.getpid()) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() + ) self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetPriorityClass(handle) psutil_value = psutil.Process().nice() self.assertEqual(psutil_value, sys_value) def test_memory_info(self): - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, self.pid) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid + ) self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetProcessMemoryInfo(handle) psutil_value = psutil.Process(self.pid).memory_info() self.assertEqual( - sys_value['PeakWorkingSetSize'], psutil_value.peak_wset) + sys_value['PeakWorkingSetSize'], psutil_value.peak_wset + ) + self.assertEqual(sys_value['WorkingSetSize'], psutil_value.wset) self.assertEqual( - sys_value['WorkingSetSize'], psutil_value.wset) + sys_value['QuotaPeakPagedPoolUsage'], psutil_value.peak_paged_pool + ) self.assertEqual( - sys_value['QuotaPeakPagedPoolUsage'], psutil_value.peak_paged_pool) - self.assertEqual( - sys_value['QuotaPagedPoolUsage'], psutil_value.paged_pool) + sys_value['QuotaPagedPoolUsage'], psutil_value.paged_pool + ) self.assertEqual( sys_value['QuotaPeakNonPagedPoolUsage'], - psutil_value.peak_nonpaged_pool) - self.assertEqual( - sys_value['QuotaNonPagedPoolUsage'], psutil_value.nonpaged_pool) + psutil_value.peak_nonpaged_pool, + ) self.assertEqual( - sys_value['PagefileUsage'], psutil_value.pagefile) + sys_value['QuotaNonPagedPoolUsage'], psutil_value.nonpaged_pool + ) + self.assertEqual(sys_value['PagefileUsage'], psutil_value.pagefile) self.assertEqual( - sys_value['PeakPagefileUsage'], psutil_value.peak_pagefile) + sys_value['PeakPagefileUsage'], psutil_value.peak_pagefile + ) self.assertEqual(psutil_value.rss, psutil_value.wset) self.assertEqual(psutil_value.vms, psutil_value.pagefile) def test_wait(self): - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, self.pid) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid + ) self.addCleanup(win32api.CloseHandle, handle) p = psutil.Process(self.pid) p.terminate() @@ -493,44 +534,56 @@ def test_cpu_affinity(self): def from_bitmask(x): return [i for i in range(64) if (1 << i) & x] - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, self.pid) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, self.pid + ) self.addCleanup(win32api.CloseHandle, handle) sys_value = from_bitmask( - win32process.GetProcessAffinityMask(handle)[0]) + win32process.GetProcessAffinityMask(handle)[0] + ) psutil_value = psutil.Process(self.pid).cpu_affinity() self.assertEqual(psutil_value, sys_value) def test_io_counters(self): - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, os.getpid()) + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, win32con.FALSE, os.getpid() + ) self.addCleanup(win32api.CloseHandle, handle) sys_value = win32process.GetProcessIoCounters(handle) psutil_value = psutil.Process().io_counters() self.assertEqual( - psutil_value.read_count, sys_value['ReadOperationCount']) + psutil_value.read_count, sys_value['ReadOperationCount'] + ) self.assertEqual( - psutil_value.write_count, sys_value['WriteOperationCount']) + psutil_value.write_count, sys_value['WriteOperationCount'] + ) self.assertEqual( - psutil_value.read_bytes, sys_value['ReadTransferCount']) + psutil_value.read_bytes, sys_value['ReadTransferCount'] + ) self.assertEqual( - psutil_value.write_bytes, sys_value['WriteTransferCount']) + psutil_value.write_bytes, sys_value['WriteTransferCount'] + ) self.assertEqual( - psutil_value.other_count, sys_value['OtherOperationCount']) + psutil_value.other_count, sys_value['OtherOperationCount'] + ) self.assertEqual( - psutil_value.other_bytes, sys_value['OtherTransferCount']) + psutil_value.other_bytes, sys_value['OtherTransferCount'] + ) def test_num_handles(self): import ctypes import ctypes.wintypes + PROCESS_QUERY_INFORMATION = 0x400 handle = ctypes.windll.kernel32.OpenProcess( - PROCESS_QUERY_INFORMATION, 0, self.pid) + PROCESS_QUERY_INFORMATION, 0, self.pid + ) self.addCleanup(ctypes.windll.kernel32.CloseHandle, handle) hndcnt = ctypes.wintypes.DWORD() ctypes.windll.kernel32.GetProcessHandleCount( - handle, ctypes.byref(hndcnt)) + handle, ctypes.byref(hndcnt) + ) sys_value = hndcnt.value psutil_value = psutil.Process(self.pid).num_handles() self.assertEqual(psutil_value, sys_value) @@ -581,8 +634,7 @@ def test_exe(self): def test_cmdline(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) - self.assertEqual(' '.join(p.cmdline()), - w.CommandLine.replace('"', '')) + self.assertEqual(' '.join(p.cmdline()), w.CommandLine.replace('"', '')) def test_username(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] @@ -615,8 +667,9 @@ def test_create_time(self): w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] p = psutil.Process(self.pid) wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime("%Y%m%d%H%M%S", - time.localtime(p.create_time())) + psutil_create = time.strftime( + "%Y%m%d%H%M%S", time.localtime(p.create_time()) + ) self.assertEqual(wmic_create, psutil_create) @@ -644,8 +697,10 @@ def tearDownClass(cls): def test_memory_info(self): mem_1 = psutil.Process(self.pid).memory_info() - with mock.patch("psutil._psplatform.cext.proc_memory_info", - side_effect=OSError(errno.EPERM, "msg")) as fun: + with mock.patch( + "psutil._psplatform.cext.proc_memory_info", + side_effect=OSError(errno.EPERM, "msg"), + ) as fun: mem_2 = psutil.Process(self.pid).memory_info() self.assertEqual(len(mem_1), len(mem_2)) for i in range(len(mem_1)): @@ -656,38 +711,50 @@ def test_memory_info(self): def test_create_time(self): ctime = psutil.Process(self.pid).create_time() - with mock.patch("psutil._psplatform.cext.proc_times", - side_effect=OSError(errno.EPERM, "msg")) as fun: + with mock.patch( + "psutil._psplatform.cext.proc_times", + side_effect=OSError(errno.EPERM, "msg"), + ) as fun: self.assertEqual(psutil.Process(self.pid).create_time(), ctime) assert fun.called def test_cpu_times(self): cpu_times_1 = psutil.Process(self.pid).cpu_times() - with mock.patch("psutil._psplatform.cext.proc_times", - side_effect=OSError(errno.EPERM, "msg")) as fun: + with mock.patch( + "psutil._psplatform.cext.proc_times", + side_effect=OSError(errno.EPERM, "msg"), + ) as fun: cpu_times_2 = psutil.Process(self.pid).cpu_times() assert fun.called self.assertAlmostEqual( - cpu_times_1.user, cpu_times_2.user, delta=0.01) + cpu_times_1.user, cpu_times_2.user, delta=0.01 + ) self.assertAlmostEqual( - cpu_times_1.system, cpu_times_2.system, delta=0.01) + cpu_times_1.system, cpu_times_2.system, delta=0.01 + ) def test_io_counters(self): io_counters_1 = psutil.Process(self.pid).io_counters() - with mock.patch("psutil._psplatform.cext.proc_io_counters", - side_effect=OSError(errno.EPERM, "msg")) as fun: + with mock.patch( + "psutil._psplatform.cext.proc_io_counters", + side_effect=OSError(errno.EPERM, "msg"), + ) as fun: io_counters_2 = psutil.Process(self.pid).io_counters() for i in range(len(io_counters_1)): self.assertAlmostEqual( - io_counters_1[i], io_counters_2[i], delta=5) + io_counters_1[i], io_counters_2[i], delta=5 + ) assert fun.called def test_num_handles(self): num_handles = psutil.Process(self.pid).num_handles() - with mock.patch("psutil._psplatform.cext.proc_num_handles", - side_effect=OSError(errno.EPERM, "msg")) as fun: - self.assertEqual(psutil.Process(self.pid).num_handles(), - num_handles) + with mock.patch( + "psutil._psplatform.cext.proc_num_handles", + side_effect=OSError(errno.EPERM, "msg"), + ) as fun: + self.assertEqual( + psutil.Process(self.pid).num_handles(), num_handles + ) assert fun.called def test_cmdline(self): @@ -697,8 +764,9 @@ def test_cmdline(self): b = cext.proc_cmdline(pid, use_peb=False) except OSError as err: err = convert_oserror(err) - if not isinstance(err, (psutil.AccessDenied, - psutil.NoSuchProcess)): + if not isinstance( + err, (psutil.AccessDenied, psutil.NoSuchProcess) + ): raise else: self.assertEqual(a, b) @@ -720,9 +788,11 @@ def find_other_interpreter(): # XXX: a different and probably more stable approach might be to access # the registry but accessing 64 bit paths from a 32 bit process for filename in glob.glob(r"C:\Python*\python.exe"): - proc = subprocess.Popen(args=[filename, "-c", code], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + proc = subprocess.Popen( + args=[filename, "-c", code], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) output, _ = proc.communicate() proc.wait() if output == str(not IS_64BIT): @@ -736,7 +806,8 @@ def setUp(self): other_python = self.find_other_interpreter() if other_python is None: raise unittest.SkipTest( - "could not find interpreter with opposite bitness") + "could not find interpreter with opposite bitness" + ) if IS_64BIT: self.python64 = sys.executable self.python32 = other_python @@ -747,13 +818,11 @@ def setUp(self): env = os.environ.copy() env["THINK_OF_A_NUMBER"] = str(os.getpid()) self.proc32 = self.spawn_testproc( - [self.python32] + self.test_args, - env=env, - stdin=subprocess.PIPE) + [self.python32] + self.test_args, env=env, stdin=subprocess.PIPE + ) self.proc64 = self.spawn_testproc( - [self.python64] + self.test_args, - env=env, - stdin=subprocess.PIPE) + [self.python64] + self.test_args, env=env, stdin=subprocess.PIPE + ) def tearDown(self): super().tearDown() @@ -799,7 +868,6 @@ def test_environ_64(self): @unittest.skipIf(not WINDOWS, "WINDOWS only") class TestServices(PsutilTestCase): - def test_win_service_iter(self): valid_statuses = set([ "running", @@ -810,11 +878,7 @@ def test_win_service_iter(self): "stop", "stopped", ]) - valid_start_types = set([ - "automatic", - "manual", - "disabled", - ]) + valid_start_types = set(["automatic", "manual", "disabled"]) valid_statuses = set([ "running", "paused", @@ -849,8 +913,9 @@ def test_win_service_iter(self): self.assertEqual(serv, s) def test_win_service_get(self): - ERROR_SERVICE_DOES_NOT_EXIST = \ + ERROR_SERVICE_DOES_NOT_EXIST = ( psutil._psplatform.cext.ERROR_SERVICE_DOES_NOT_EXIST + ) ERROR_ACCESS_DENIED = psutil._psplatform.cext.ERROR_ACCESS_DENIED name = next(psutil.win_service_iter()).name() @@ -865,11 +930,13 @@ def test_win_service_get(self): else: args = (ERROR_SERVICE_DOES_NOT_EXIST, "msg") exc = WindowsError(*args) - with mock.patch("psutil._psplatform.cext.winservice_query_status", - side_effect=exc): + with mock.patch( + "psutil._psplatform.cext.winservice_query_status", side_effect=exc + ): self.assertRaises(psutil.NoSuchProcess, service.status) - with mock.patch("psutil._psplatform.cext.winservice_query_config", - side_effect=exc): + with mock.patch( + "psutil._psplatform.cext.winservice_query_config", side_effect=exc + ): self.assertRaises(psutil.NoSuchProcess, service.username) # test AccessDenied @@ -878,11 +945,13 @@ def test_win_service_get(self): else: args = (ERROR_ACCESS_DENIED, "msg") exc = WindowsError(*args) - with mock.patch("psutil._psplatform.cext.winservice_query_status", - side_effect=exc): + with mock.patch( + "psutil._psplatform.cext.winservice_query_status", side_effect=exc + ): self.assertRaises(psutil.AccessDenied, service.status) - with mock.patch("psutil._psplatform.cext.winservice_query_config", - side_effect=exc): + with mock.patch( + "psutil._psplatform.cext.winservice_query_config", side_effect=exc + ): self.assertRaises(psutil.AccessDenied, service.username) # test __str__ and __repr__ @@ -894,4 +963,5 @@ def test_win_service_get(self): if __name__ == '__main__': from psutil.tests.runner import run_from_name + run_from_name(__file__) diff --git a/pyproject.toml b/pyproject.toml index 6e5e9563a..677722dbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,9 @@ +[tool.black] +line-length = 79 +skip-string-normalization = true +preview = true +target-version = ["py37"] + [tool.ruff] # https://beta.ruff.rs/docs/settings/ target-version = "py37" @@ -25,6 +31,7 @@ ignore = [ "B904", # Within an `except` clause, raise exceptions with `raise ... from err` (PYTHON2.7 COMPAT) "C4", # flake8-comprehensions (PYTHON2.7 COMPAT) "C90", # mccabe (function `X` is too complex) + "COM812", # Trailing comma missing "D", # pydocstyle "DTZ", # flake8-datetimez "ERA001", # Found commented-out code diff --git a/scripts/battery.py b/scripts/battery.py index 040f94819..0595d1ad1 100755 --- a/scripts/battery.py +++ b/scripts/battery.py @@ -35,8 +35,10 @@ def main(): print("charge: %s%%" % round(batt.percent, 2)) if batt.power_plugged: - print("status: %s" % ( - "charging" if batt.percent < 100 else "fully charged")) + print( + "status: %s" + % ("charging" if batt.percent < 100 else "fully charged") + ) print("plugged in: yes") else: print("left: %s" % secs2hours(batt.secsleft)) diff --git a/scripts/disk_usage.py b/scripts/disk_usage.py index d801c6ddd..934b24a1c 100755 --- a/scripts/disk_usage.py +++ b/scripts/disk_usage.py @@ -23,8 +23,7 @@ def main(): templ = "%-17s %8s %8s %8s %5s%% %9s %s" - print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", - "Mount")) + print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", "Mount")) for part in psutil.disk_partitions(all=False): if os.name == 'nt': if 'cdrom' in part.opts or part.fstype == '': @@ -33,14 +32,16 @@ def main(): # partition or just hang. continue usage = psutil.disk_usage(part.mountpoint) - print(templ % ( + line = templ % ( part.device, bytes2human(usage.total), bytes2human(usage.used), bytes2human(usage.free), int(usage.percent), part.fstype, - part.mountpoint)) + part.mountpoint, + ) + print(line) if __name__ == '__main__': diff --git a/scripts/free.py b/scripts/free.py index f72149ac3..332507284 100755 --- a/scripts/free.py +++ b/scripts/free.py @@ -20,21 +20,26 @@ def main(): swap = psutil.swap_memory() templ = "%-7s %10s %10s %10s %10s %10s %10s" print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache')) - print(templ % ( + sect = templ % ( 'Mem:', int(virt.total / 1024), int(virt.used / 1024), int(virt.free / 1024), int(getattr(virt, 'shared', 0) / 1024), int(getattr(virt, 'buffers', 0) / 1024), - int(getattr(virt, 'cached', 0) / 1024))) - print(templ % ( - 'Swap:', int(swap.total / 1024), + int(getattr(virt, 'cached', 0) / 1024), + ) + print(sect) + sect = templ % ( + 'Swap:', + int(swap.total / 1024), int(swap.used / 1024), int(swap.free / 1024), '', '', - '')) + '', + ) + print(sect) if __name__ == '__main__': diff --git a/scripts/ifconfig.py b/scripts/ifconfig.py index 7fdfa1e12..4b4246aaa 100755 --- a/scripts/ifconfig.py +++ b/scripts/ifconfig.py @@ -71,19 +71,37 @@ def main(): if nic in stats: st = stats[nic] print(" stats : ", end='') - print("speed=%sMB, duplex=%s, mtu=%s, up=%s" % ( - st.speed, duplex_map[st.duplex], st.mtu, - "yes" if st.isup else "no")) + print( + "speed=%sMB, duplex=%s, mtu=%s, up=%s" + % ( + st.speed, + duplex_map[st.duplex], + st.mtu, + "yes" if st.isup else "no", + ) + ) if nic in io_counters: io = io_counters[nic] print(" incoming : ", end='') - print("bytes=%s, pkts=%s, errs=%s, drops=%s" % ( - bytes2human(io.bytes_recv), io.packets_recv, io.errin, - io.dropin)) + print( + "bytes=%s, pkts=%s, errs=%s, drops=%s" + % ( + bytes2human(io.bytes_recv), + io.packets_recv, + io.errin, + io.dropin, + ) + ) print(" outgoing : ", end='') - print("bytes=%s, pkts=%s, errs=%s, drops=%s" % ( - bytes2human(io.bytes_sent), io.packets_sent, io.errout, - io.dropout)) + print( + "bytes=%s, pkts=%s, errs=%s, drops=%s" + % ( + bytes2human(io.bytes_sent), + io.packets_sent, + io.errout, + io.dropout, + ) + ) for addr in addrs: print(" %-4s" % af_map.get(addr.family, addr.family), end="") print(" address : %s" % addr.address) diff --git a/scripts/internal/bench_oneshot.py b/scripts/internal/bench_oneshot.py index 74f8150ae..b58224bb6 100755 --- a/scripts/internal/bench_oneshot.py +++ b/scripts/internal/bench_oneshot.py @@ -127,19 +127,23 @@ def call_oneshot(funs): def main(): - print("%s methods involved on platform %r (%s iterations, psutil %s):" % ( - len(names), sys.platform, ITERATIONS, psutil.__version__)) + print( + "%s methods involved on platform %r (%s iterations, psutil %s):" + % (len(names), sys.platform, ITERATIONS, psutil.__version__) + ) for name in sorted(names): print(" " + name) # "normal" run elapsed1 = timeit.timeit( - "call_normal(funs)", setup=setup, number=ITERATIONS) + "call_normal(funs)", setup=setup, number=ITERATIONS + ) print("normal: %.3f secs" % elapsed1) # "one shot" run elapsed2 = timeit.timeit( - "call_oneshot(funs)", setup=setup, number=ITERATIONS) + "call_oneshot(funs)", setup=setup, number=ITERATIONS + ) print("onshot: %.3f secs" % elapsed2) # done diff --git a/scripts/internal/bench_oneshot_2.py b/scripts/internal/bench_oneshot_2.py index 2a63dca25..fe5151d3e 100755 --- a/scripts/internal/bench_oneshot_2.py +++ b/scripts/internal/bench_oneshot_2.py @@ -40,8 +40,10 @@ def main(): args = runner.parse_args() if not args.worker: - print("%s methods involved on platform %r (psutil %s):" % ( - len(names), sys.platform, psutil.__version__)) + print( + "%s methods involved on platform %r (psutil %s):" + % (len(names), sys.platform, psutil.__version__) + ) for name in sorted(names): print(" " + name) diff --git a/scripts/internal/check_broken_links.py b/scripts/internal/check_broken_links.py index af53b878c..90cb258c3 100755 --- a/scripts/internal/check_broken_links.py +++ b/scripts/internal/check_broken_links.py @@ -54,7 +54,8 @@ HERE = os.path.abspath(os.path.dirname(__file__)) REGEX = re.compile( r'(?:http|ftp|https)?://' - r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+') + r'(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+' +) REQUEST_TIMEOUT = 15 # There are some status codes sent by websites on HEAD request. # Like 503 by Microsoft, and 401 by Apple @@ -64,6 +65,7 @@ def memoize(fun): """A memoize decorator.""" + @functools.wraps(fun) def wrapper(*args, **kwargs): key = (args, frozenset(sorted(kwargs.items()))) @@ -102,8 +104,10 @@ def parse_rst(fname): # HISTORY file has a lot of dead links. if fname == 'HISTORY.rst' and urls: urls = [ - x for x in urls if - not x.startswith('https://github.com/giampaolo/psutil/issues')] + x + for x in urls + if not x.startswith('https://github.com/giampaolo/psutil/issues') + ] return urls @@ -204,8 +208,9 @@ def parallel_validator(urls): current = 0 total = len(urls) with concurrent.futures.ThreadPoolExecutor() as executor: - fut_to_url = {executor.submit(validate_url, url[1]): url - for url in urls} + fut_to_url = { + executor.submit(validate_url, url[1]): url for url in urls + } for fut in concurrent.futures.as_completed(fut_to_url): current += 1 sys.stdout.write("\r%s / %s" % (current, total)) @@ -228,7 +233,8 @@ def parallel_validator(urls): def main(): parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + description=__doc__, formatter_class=argparse.RawTextHelpFormatter + ) parser.add_argument('files', nargs="+") parser.parse_args() args = parser.parse_args() diff --git a/scripts/internal/clinter.py b/scripts/internal/clinter.py index 71c77e402..516ca894e 100755 --- a/scripts/internal/clinter.py +++ b/scripts/internal/clinter.py @@ -36,9 +36,11 @@ def check_line(path, line, idx, lines): if not eof: nextline = lines[idx + 1] # "#" is a pre-processor line - if nextline != '\n' and \ - nextline.strip()[0] != '#' and \ - nextline.strip()[:2] != '*/': + if ( + nextline != '\n' + and nextline.strip()[0] != '#' + and nextline.strip()[:2] != '*/' + ): warn(path, line, lineno, "expected 1 blank line") sls = s.lstrip() diff --git a/scripts/internal/download_wheels_appveyor.py b/scripts/internal/download_wheels_appveyor.py index 47a33d996..e8b0c54e0 100755 --- a/scripts/internal/download_wheels_appveyor.py +++ b/scripts/internal/download_wheels_appveyor.py @@ -40,7 +40,7 @@ def download_file(url): tot_bytes = 0 with open(local_fname, 'wb') as f: for chunk in r.iter_content(chunk_size=16384): - if chunk: # filter out keep-alive new chunks + if chunk: # filter out keep-alive new chunks f.write(chunk) tot_bytes += len(chunk) return local_fname @@ -49,8 +49,8 @@ def download_file(url): def get_file_urls(): with requests.Session() as session: data = session.get( - BASE_URL + '/projects/' + USER + '/' + PROJECT, - timeout=TIMEOUT) + BASE_URL + '/projects/' + USER + '/' + PROJECT, timeout=TIMEOUT + ) data = data.json() urls = [] @@ -96,8 +96,10 @@ def run(): raise else: completed += 1 - print("downloaded %-45s %s" % ( - local_fname, bytes2human(os.path.getsize(local_fname)))) + print( + "downloaded %-45s %s" + % (local_fname, bytes2human(os.path.getsize(local_fname))) + ) # 2 wheels (32 and 64 bit) per supported python version expected = len(PY_VERSIONS) * 2 if expected != completed: diff --git a/scripts/internal/download_wheels_github.py b/scripts/internal/download_wheels_github.py index de6c34faa..c6ecb5dda 100755 --- a/scripts/internal/download_wheels_github.py +++ b/scripts/internal/download_wheels_github.py @@ -38,8 +38,9 @@ def get_artifacts(): base_url = "https://api.github.com/repos/%s/%s" % (USER, PROJECT) url = base_url + "/actions/artifacts" - res = requests.get(url=url, headers={ - "Authorization": "token %s" % TOKEN}, timeout=TIMEOUT) + res = requests.get( + url=url, headers={"Authorization": "token %s" % TOKEN}, timeout=TIMEOUT + ) res.raise_for_status() data = json.loads(res.content) return data @@ -47,8 +48,9 @@ def get_artifacts(): def download_zip(url): print("downloading: " + url) - res = requests.get(url=url, headers={ - "Authorization": "token %s" % TOKEN}, timeout=TIMEOUT) + res = requests.get( + url=url, headers={"Authorization": "token %s" % TOKEN}, timeout=TIMEOUT + ) res.raise_for_status() totbytes = 0 with open(OUTFILE, 'wb') as f: diff --git a/scripts/internal/generate_manifest.py b/scripts/internal/generate_manifest.py index 290e8b49f..9eeeb1a24 100755 --- a/scripts/internal/generate_manifest.py +++ b/scripts/internal/generate_manifest.py @@ -12,21 +12,24 @@ SKIP_EXTS = ('.png', '.jpg', '.jpeg', '.svg') -SKIP_FILES = ('appveyor.yml') +SKIP_FILES = 'appveyor.yml' SKIP_PREFIXES = ('.ci/', '.github/') def sh(cmd): return subprocess.check_output( - shlex.split(cmd), universal_newlines=True).strip() + shlex.split(cmd), universal_newlines=True + ).strip() def main(): files = set() for file in sh("git ls-files").split('\n'): - if file.startswith(SKIP_PREFIXES) or \ - os.path.splitext(file)[1].lower() in SKIP_EXTS or \ - file in SKIP_FILES: + if ( + file.startswith(SKIP_PREFIXES) + or os.path.splitext(file)[1].lower() in SKIP_EXTS + or file in SKIP_FILES + ): continue files.add(file) diff --git a/scripts/internal/git_pre_commit.py b/scripts/internal/git_pre_commit.py index 79582feeb..84c38cccd 100755 --- a/scripts/internal/git_pre_commit.py +++ b/scripts/internal/git_pre_commit.py @@ -26,6 +26,7 @@ def term_supports_colors(): try: import curses + assert sys.stderr.isatty() curses.setupterm() assert curses.tigetnum("colors") > 0 @@ -41,9 +42,9 @@ def hilite(s, ok=True, bold=False): attr = [] if ok is None: # no color pass - elif ok: # green + elif ok: # green attr.append('32') - else: # red + else: # red attr.append('31') if bold: attr.append('1') @@ -59,7 +60,9 @@ def sh(cmd): if isinstance(cmd, str): cmd = shlex.split(cmd) p = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True, ) stdout, stderr = p.communicate() @@ -79,30 +82,45 @@ def open_text(path): def git_commit_files(): out = sh(["git", "diff", "--cached", "--name-only"]) - py_files = [x for x in out.split('\n') if x.endswith('.py') and - os.path.exists(x)] - c_files = [x for x in out.split('\n') if x.endswith(('.c', '.h')) and - os.path.exists(x)] - rst_files = [x for x in out.split('\n') if x.endswith('.rst') and - os.path.exists(x)] + py_files = [ + x for x in out.split('\n') if x.endswith('.py') and os.path.exists(x) + ] + c_files = [ + x + for x in out.split('\n') + if x.endswith(('.c', '.h')) and os.path.exists(x) + ] + rst_files = [ + x for x in out.split('\n') if x.endswith('.rst') and os.path.exists(x) + ] toml_files = [ x for x in out.split("\n") if x.endswith(".toml") and os.path.exists(x) ] new_rm_mv = sh( - ["git", "diff", "--name-only", "--diff-filter=ADR", "--cached"], + ["git", "diff", "--name-only", "--diff-filter=ADR", "--cached"] ) # XXX: we should escape spaces and possibly other amenities here new_rm_mv = new_rm_mv.split() return (py_files, c_files, rst_files, toml_files, new_rm_mv) +def black(files): + print("running black (%s)" % len(files)) + cmd = [PYTHON, "-m", "black", "--check", "--safe"] + files + if subprocess.call(cmd) != 0: + return exit( + "Python code didn't pass 'ruff' style check." + "Try running 'make fix-ruff'." + ) + + def ruff(files): print("running ruff (%s)" % len(files)) cmd = [PYTHON, "-m", "ruff", "check", "--no-cache"] + files if subprocess.call(cmd) != 0: return exit( "Python code didn't pass 'ruff' style check." - "Try running 'make fix-ruff'.", + "Try running 'make fix-ruff'." ) @@ -131,6 +149,7 @@ def rstcheck(files): def main(): py_files, c_files, rst_files, toml_files, new_rm_mv = git_commit_files() if py_files: + black(py_files) ruff(py_files) if c_files: c_linter(c_files) @@ -142,8 +161,10 @@ def main(): out = sh([PYTHON, "scripts/internal/generate_manifest.py"]) with open_text('MANIFEST.in') as f: if out.strip() != f.read().strip(): - sys.exit("some files were added, deleted or renamed; " - "run 'make generate-manifest' and commit again") + sys.exit( + "some files were added, deleted or renamed; " + "run 'make generate-manifest' and commit again" + ) main() diff --git a/scripts/internal/print_access_denied.py b/scripts/internal/print_access_denied.py index 7759ca7b2..f7017b04d 100755 --- a/scripts/internal/print_access_denied.py +++ b/scripts/internal/print_access_denied.py @@ -84,9 +84,10 @@ def main(): print_color(s, "red" if ads else None) tot_perc = round((tot_ads / tot_calls) * 100, 1) print("-" * 50) - print("Totals: access-denied=%s (%s%%), calls=%s, processes=%s, " - "elapsed=%ss" % (tot_ads, tot_perc, tot_calls, tot_procs, - round(elapsed, 2))) + print( + "Totals: access-denied=%s (%s%%), calls=%s, processes=%s, elapsed=%ss" + % (tot_ads, tot_perc, tot_calls, tot_procs, round(elapsed, 2)) + ) if __name__ == '__main__': diff --git a/scripts/internal/print_announce.py b/scripts/internal/print_announce.py index 2297c0950..9f89f635a 100755 --- a/scripts/internal/print_announce.py +++ b/scripts/internal/print_announce.py @@ -20,15 +20,17 @@ ROOT = os.path.realpath(os.path.join(HERE, '..', '..')) HISTORY = os.path.join(ROOT, 'HISTORY.rst') PRINT_HASHES_SCRIPT = os.path.join( - ROOT, 'scripts', 'internal', 'print_hashes.py') + ROOT, 'scripts', 'internal', 'print_hashes.py' +) PRJ_NAME = 'psutil' PRJ_VERSION = __version__ PRJ_URL_HOME = 'https://github.com/giampaolo/psutil' PRJ_URL_DOC = 'http://psutil.readthedocs.io' PRJ_URL_DOWNLOAD = 'https://pypi.org/project/psutil/#files' -PRJ_URL_WHATSNEW = \ +PRJ_URL_WHATSNEW = ( 'https://github.com/giampaolo/psutil/blob/master/HISTORY.rst' +) template = """\ Hello all, @@ -109,9 +111,12 @@ def get_changes(): def main(): changes = get_changes() - hashes = subprocess.check_output( - [sys.executable, PRINT_HASHES_SCRIPT, 'dist/']).strip().decode() - print(template.format( + hashes = ( + subprocess.check_output([sys.executable, PRINT_HASHES_SCRIPT, 'dist/']) + .strip() + .decode() + ) + text = template.format( prj_name=PRJ_NAME, prj_version=PRJ_VERSION, prj_urlhome=PRJ_URL_HOME, @@ -120,7 +125,8 @@ def main(): prj_urlwhatsnew=PRJ_URL_WHATSNEW, changes=changes, hashes=hashes, - )) + ) + print(text) if __name__ == '__main__': diff --git a/scripts/internal/print_api_speed.py b/scripts/internal/print_api_speed.py index 8abaed0c4..adbaa89a0 100755 --- a/scripts/internal/print_api_speed.py +++ b/scripts/internal/print_api_speed.py @@ -136,7 +136,8 @@ def main(): global TIMES parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawTextHelpFormatter) + description=__doc__, formatter_class=argparse.RawTextHelpFormatter + ) parser.add_argument('-t', '--times', type=int, default=TIMES) args = parser.parse_args() TIMES = args.times @@ -152,8 +153,12 @@ def main(): # --- system public_apis = [] - ignore = ['wait_procs', 'process_iter', 'win_service_get', - 'win_service_iter'] + ignore = [ + 'wait_procs', + 'process_iter', + 'win_service_get', + 'win_service_iter', + ] if psutil.MACOS: ignore.append('net_connections') # raises AD for name in psutil.__all__: @@ -167,9 +172,9 @@ def main(): fun = getattr(psutil, name) args = () if name == 'pid_exists': - args = (os.getpid(), ) + args = (os.getpid(),) elif name == 'disk_usage': - args = (os.getcwd(), ) + args = (os.getcwd(),) timecall(name, fun, *args) timecall('cpu_count (cores)', psutil.cpu_count, logical=False) timecall('process_iter (all)', lambda: list(psutil.process_iter())) @@ -178,9 +183,22 @@ def main(): # --- process print("") print_header("PROCESS APIS") - ignore = ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait', - 'as_dict', 'parent', 'parents', 'memory_info_ex', 'oneshot', - 'pid', 'rlimit', 'children'] + ignore = [ + 'send_signal', + 'suspend', + 'resume', + 'terminate', + 'kill', + 'wait', + 'as_dict', + 'parent', + 'parents', + 'memory_info_ex', + 'oneshot', + 'pid', + 'rlimit', + 'children', + ] if psutil.MACOS: ignore.append('memory_maps') # XXX p = psutil.Process() diff --git a/scripts/internal/print_dist.py b/scripts/internal/print_dist.py index 740951ac0..5b6531608 100755 --- a/scripts/internal/print_dist.py +++ b/scripts/internal/print_dist.py @@ -15,15 +15,18 @@ class Wheel: - def __init__(self, path): self._path = path self._name = os.path.basename(path) def __repr__(self): return "<%s(name=%s, plat=%s, arch=%s, pyver=%s)>" % ( - self.__class__.__name__, self.name, self.platform(), self.arch(), - self.pyver()) + self.__class__.__name__, + self.name, + self.platform(), + self.arch(), + self.pyver(), + ) __str__ = __repr__ @@ -72,7 +75,6 @@ def size(self): class Tarball(Wheel): - def platform(self): return "source" @@ -85,8 +87,12 @@ def pyver(self): def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('dir', nargs="?", default="dist", - help='directory containing tar.gz or wheel files') + parser.add_argument( + 'dir', + nargs="?", + default="dist", + help='directory containing tar.gz or wheel files', + ) args = parser.parse_args() groups = collections.defaultdict(list) @@ -111,15 +117,21 @@ def main(): for pkg in sorted(pkgs, key=lambda x: x.name): tot_files += 1 tot_size += pkg.size() - s = templ % (" " + pkg.name, bytes2human(pkg.size()), pkg.arch(), - pkg.pyver()) + s = templ % ( + " " + pkg.name, + bytes2human(pkg.size()), + pkg.arch(), + pkg.pyver(), + ) if 'pypy' in pkg.pyver(): print_color(s, color='violet') else: print_color(s, color='brown') - print_color("\n\ntotals: files=%s, size=%s" % ( - tot_files, bytes2human(tot_size)), bold=True) + print_color( + "\n\ntotals: files=%s, size=%s" % (tot_files, bytes2human(tot_size)), + bold=True, + ) if __name__ == '__main__': diff --git a/scripts/internal/print_downloads.py b/scripts/internal/print_downloads.py index 8e47a08b4..09169984d 100755 --- a/scripts/internal/print_downloads.py +++ b/scripts/internal/print_downloads.py @@ -38,13 +38,18 @@ # --- get + @memoize def sh(cmd): assert os.path.exists(AUTH_FILE) env = os.environ.copy() env['GOOGLE_APPLICATION_CREDENTIALS'] = AUTH_FILE - p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, - stderr=subprocess.PIPE, universal_newlines=True) + p = subprocess.Popen( + shlex.split(cmd), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) stdout, stderr = p.communicate() if p.returncode != 0: raise RuntimeError(stderr) @@ -62,8 +67,9 @@ def query(cmd): def top_packages(): global LAST_UPDATE - ret = query("pypinfo --all --json --days %s --limit %s '' project" % ( - DAYS, LIMIT)) + ret = query( + "pypinfo --all --json --days %s --limit %s '' project" % (DAYS, LIMIT) + ) LAST_UPDATE = ret['last_update'] return [(x['project'], x['download_count']) for x in ret['rows']] @@ -147,14 +153,18 @@ def main(): {'what': 'PYPI ranking', 'download_count': ranking()}, ] print_markdown_table('Overview', 'what', data) - print_markdown_table('Operating systems', 'system_name', - downloads_by_system()['rows']) - print_markdown_table('Distros', 'distro_name', - downloads_by_distro()['rows']) - print_markdown_table('Python versions', 'python_version', - downloads_pyver()['rows']) - print_markdown_table('Countries', 'country', - downloads_by_country()['rows']) + print_markdown_table( + 'Operating systems', 'system_name', downloads_by_system()['rows'] + ) + print_markdown_table( + 'Distros', 'distro_name', downloads_by_distro()['rows'] + ) + print_markdown_table( + 'Python versions', 'python_version', downloads_pyver()['rows'] + ) + print_markdown_table( + 'Countries', 'country', downloads_by_country()['rows'] + ) if __name__ == '__main__': diff --git a/scripts/internal/print_hashes.py b/scripts/internal/print_hashes.py index 68e06201c..5b9cfb209 100755 --- a/scripts/internal/print_hashes.py +++ b/scripts/internal/print_hashes.py @@ -22,16 +22,19 @@ def csum(file, kind): def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('dir', type=str, - help='directory containing tar.gz or wheel files') + parser.add_argument( + 'dir', type=str, help='directory containing tar.gz or wheel files' + ) args = parser.parse_args() for name in sorted(os.listdir(args.dir)): file = os.path.join(args.dir, name) if os.path.isfile(file): md5 = csum(file, "md5") sha256 = csum(file, "sha256") - print("%s\nmd5: %s\nsha256: %s\n" % ( - os.path.basename(file), md5, sha256)) + print( + "%s\nmd5: %s\nsha256: %s\n" + % (os.path.basename(file), md5, sha256) + ) else: print("skipping %r (not a file)" % file) diff --git a/scripts/internal/print_timeline.py b/scripts/internal/print_timeline.py index a046e670a..706601943 100755 --- a/scripts/internal/print_timeline.py +++ b/scripts/internal/print_timeline.py @@ -19,7 +19,8 @@ def sh(cmd): return subprocess.check_output( - shlex.split(cmd), universal_newlines=True).strip() + shlex.split(cmd), universal_newlines=True + ).strip() def get_tag_date(tag): diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 890a3a415..158c46eec 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -93,7 +93,7 @@ def safe_print(text, file=sys.stdout): def stderr_handle(): GetStdHandle = ctypes.windll.Kernel32.GetStdHandle - STD_ERROR_HANDLE_ID = ctypes.c_ulong(0xfffffff4) + STD_ERROR_HANDLE_ID = ctypes.c_ulong(0xFFFFFFF4) GetStdHandle.restype = ctypes.c_ulong handle = GetStdHandle(STD_ERROR_HANDLE_ID) atexit.register(ctypes.windll.Kernel32.CloseHandle, handle) @@ -114,7 +114,9 @@ def win_colorprint(s, color=LIGHTBLUE): def sh(cmd, nolog=False): if not nolog: safe_print("cmd: " + cmd) - p = subprocess.Popen(cmd, shell=True, env=os.environ, cwd=os.getcwd()) # noqa + p = subprocess.Popen( + cmd, shell=True, env=os.environ, cwd=os.getcwd() # noqa + ) p.communicate() if p.returncode != 0: sys.exit(p.returncode) @@ -122,6 +124,7 @@ def sh(cmd, nolog=False): def rm(pattern, directory=False): """Recursively remove a file or dir by pattern.""" + def safe_remove(path): try: os.remove(path) @@ -458,9 +461,11 @@ def install_git_hooks(): """Install GIT pre-commit hook.""" if os.path.isdir('.git'): src = os.path.join( - ROOT_DIR, "scripts", "internal", "git_pre_commit.py") + ROOT_DIR, "scripts", "internal", "git_pre_commit.py" + ) dst = os.path.realpath( - os.path.join(ROOT_DIR, ".git", "hooks", "pre-commit")) + os.path.join(ROOT_DIR, ".git", "hooks", "pre-commit") + ) with open(src) as s: with open(dst, "w") as d: d.write(s.read()) @@ -490,8 +495,10 @@ def print_api_speed(): def download_appveyor_wheels(): """Download appveyor wheels.""" - sh("%s -Wa scripts\\internal\\download_wheels_appveyor.py " - "--user giampaolo --project psutil" % PYTHON) + sh( + "%s -Wa scripts\\internal\\download_wheels_appveyor.py " + "--user giampaolo --project psutil" % PYTHON + ) def generate_manifest(): @@ -537,9 +544,7 @@ def get_python(path): def parse_args(): parser = argparse.ArgumentParser() # option shared by all commands - parser.add_argument( - '-p', '--python', - help="use python executable path") + parser.add_argument('-p', '--python', help="use python executable path") sp = parser.add_subparsers(dest='command', title='targets') sp.add_parser('bench-oneshot', help="benchmarks for oneshot()") sp.add_parser('bench-oneshot_2', help="benchmarks for oneshot() (perf)") @@ -559,8 +564,9 @@ def parse_args(): test_by_name = sp.add_parser('test-by-name', help=" run test by name") sp.add_parser('test-connections', help="run connections tests") sp.add_parser('test-contracts', help="run contracts tests") - sp.add_parser('test-last-failed', - help="re-run tests which failed on last run") + sp.add_parser( + 'test-last-failed', help="re-run tests which failed on last run" + ) sp.add_parser('test-memleaks', help="run memory leaks tests") sp.add_parser('test-misc', help="run misc tests") sp.add_parser('test-platform', help="run windows only tests") @@ -591,7 +597,8 @@ def main(): PYTHON = get_python(args.python) if not PYTHON: return sys.exit( - "can't find any python installation matching %r" % args.python) + "can't find any python installation matching %r" % args.python + ) os.putenv('PYTHON', PYTHON) win_colorprint("using " + PYTHON) diff --git a/scripts/iotop.py b/scripts/iotop.py index a8f04c870..23c3a376e 100755 --- a/scripts/iotop.py +++ b/scripts/iotop.py @@ -118,8 +118,10 @@ def refresh_window(procs, disks_read, disks_write): templ = "%-5s %-7s %11s %11s %s" win.erase() - disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \ - % (bytes2human(disks_read), bytes2human(disks_write)) + disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" % ( + bytes2human(disks_read), + bytes2human(disks_write), + ) printl(disks_tot) header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND") @@ -131,7 +133,8 @@ def refresh_window(procs, disks_read, disks_write): p._username[:7], bytes2human(p._read_per_sec), bytes2human(p._write_per_sec), - p._cmdline) + p._cmdline, + ) try: printl(line) except curses.error: diff --git a/scripts/netstat.py b/scripts/netstat.py index 7a9b2908c..b58900937 100755 --- a/scripts/netstat.py +++ b/scripts/netstat.py @@ -38,9 +38,15 @@ def main(): templ = "%-5s %-30s %-30s %-13s %-6s %s" - print(templ % ( - "Proto", "Local address", "Remote address", "Status", "PID", - "Program name")) + header = templ % ( + "Proto", + "Local address", + "Remote address", + "Status", + "PID", + "Program name", + ) + print(header) proc_names = {} for p in psutil.process_iter(['pid', 'name']): proc_names[p.info['pid']] = p.info['name'] @@ -50,14 +56,15 @@ def main(): if c.raddr: raddr = "%s:%s" % (c.raddr) name = proc_names.get(c.pid, '?') or '' - print(templ % ( + line = templ % ( proto_map[(c.family, c.type)], laddr, raddr or AD, c.status, c.pid or AD, name[:15], - )) + ) + print(line) if __name__ == '__main__': diff --git a/scripts/nettop.py b/scripts/nettop.py index 64d408fc4..eafcd0f5d 100755 --- a/scripts/nettop.py +++ b/scripts/nettop.py @@ -80,12 +80,13 @@ def refresh_window(tot_before, tot_after, pnic_before, pnic_after): global lineno # totals - printl("total bytes: sent: %-10s received: %s" % ( - bytes2human(tot_after.bytes_sent), - bytes2human(tot_after.bytes_recv)), + printl( + "total bytes: sent: %-10s received: %s" + % ( + bytes2human(tot_after.bytes_sent), + bytes2human(tot_after.bytes_recv), + ) ) - printl("total packets: sent: %-10s received: %s" % ( - tot_after.packets_sent, tot_after.packets_recv)) # per-network interface details: let's sort network interfaces so # that the ones which generated more traffic are shown first @@ -96,6 +97,7 @@ def refresh_window(tot_before, tot_after, pnic_before, pnic_after): stats_before = pnic_before[name] stats_after = pnic_after[name] templ = "%-15s %15s %15s" + # fmt: off printl(templ % (name, "TOTAL", "PER-SEC"), highlight=True) printl(templ % ( "bytes-sent", @@ -120,6 +122,7 @@ def refresh_window(tot_before, tot_after, pnic_before, pnic_after): stats_after.packets_recv - stats_before.packets_recv, )) printl("") + # fmt: on win.refresh() lineno = 0 diff --git a/scripts/pidof.py b/scripts/pidof.py index b809fafbe..662d5d657 100755 --- a/scripts/pidof.py +++ b/scripts/pidof.py @@ -22,8 +22,11 @@ def pidof(pgname): pids = [] for proc in psutil.process_iter(['name', 'cmdline']): # search for matches in the process name and cmdline - if proc.info['name'] == pgname or \ - proc.info['cmdline'] and proc.info['cmdline'][0] == pgname: + if ( + proc.info['name'] == pgname + or proc.info['cmdline'] + and proc.info['cmdline'][0] == pgname + ): pids.append(str(proc.pid)) return pids diff --git a/scripts/pmap.py b/scripts/pmap.py index 56c1b4882..577e4b294 100755 --- a/scripts/pmap.py +++ b/scripts/pmap.py @@ -36,7 +36,7 @@ def safe_print(s): - s = s[:get_terminal_size()[0]] + s = s[: get_terminal_size()[0]] try: print(s) except UnicodeEncodeError: @@ -52,11 +52,13 @@ def main(): total_rss = 0 for m in p.memory_maps(grouped=False): total_rss += m.rss - safe_print(templ % ( + line = templ % ( m.addr.split('-')[0].zfill(16), bytes2human(m.rss), m.perms, - m.path)) + m.path, + ) + safe_print(line) print("-" * 31) print(templ % ("Total", bytes2human(total_rss), '', '')) safe_print("PID = %s, name = %s" % (p.pid, p.name())) diff --git a/scripts/procinfo.py b/scripts/procinfo.py index 562a61a46..95c99d442 100755 --- a/scripts/procinfo.py +++ b/scripts/procinfo.py @@ -132,8 +132,9 @@ def str_ntuple(nt, convert_bytes=False): if not convert_bytes: return ", ".join(["%s=%s" % (x, getattr(nt, x)) for x in nt._fields]) else: - return ", ".join(["%s=%s" % (x, bytes2human(getattr(nt, x))) - for x in nt._fields]) + return ", ".join( + ["%s=%s" % (x, bytes2human(getattr(nt, x))) for x in nt._fields] + ) def run(pid, verbose=False): @@ -156,7 +157,8 @@ def run(pid, verbose=False): pinfo['children'] = [] if pinfo['create_time']: started = datetime.datetime.fromtimestamp( - pinfo['create_time']).strftime('%Y-%m-%d %H:%M') + pinfo['create_time'] + ).strftime('%Y-%m-%d %H:%M') else: started = ACCESS_DENIED @@ -173,7 +175,8 @@ def run(pid, verbose=False): cpu_tot_time = "%s:%s.%s" % ( cpu_tot_time.seconds // 60 % 60, str(cpu_tot_time.seconds % 60).zfill(2), - str(cpu_tot_time.microseconds)[:2]) + str(cpu_tot_time.microseconds)[:2], + ) print_('cpu-tspent', cpu_tot_time) print_('cpu-times', str_ntuple(pinfo['cpu_times'])) if hasattr(proc, "cpu_affinity"): @@ -202,8 +205,10 @@ def run(pid, verbose=False): if psutil.WINDOWS: print_("ionice", ionice) else: - print_("ionice", "class=%s, value=%s" % ( - str(ionice.ioclass), ionice.value)) + print_( + "ionice", + "class=%s, value=%s" % (str(ionice.ioclass), ionice.value), + ) print_('num-threads', pinfo['num_threads']) if psutil.POSIX: @@ -238,8 +243,10 @@ def run(pid, verbose=False): if pinfo['connections']: template = '%-5s %-25s %-25s %s' - print_('connections', - template % ('PROTO', 'LOCAL ADDR', 'REMOTE ADDR', 'STATUS')) + print_( + 'connections', + template % ('PROTO', 'LOCAL ADDR', 'REMOTE ADDR', 'STATUS'), + ) for conn in pinfo['connections']: if conn.type == socket.SOCK_STREAM: type = 'TCP' @@ -252,11 +259,13 @@ def run(pid, verbose=False): rip, rport = '*', '*' else: rip, rport = conn.raddr - print_('', template % ( + line = template % ( type, "%s:%s" % (lip, lport), "%s:%s" % (rip, rport), - conn.status)) + conn.status, + ) + print_('', line) else: print_('connections', '') @@ -290,8 +299,11 @@ def run(pid, verbose=False): soft = "infinity" if hard == psutil.RLIM_INFINITY: hard = "infinity" - print_('', template % ( - RLIMITS_MAP.get(res_name, res_name), soft, hard)) + print_( + '', + template + % (RLIMITS_MAP.get(res_name, res_name), soft, hard), + ) if hasattr(proc, "environ") and pinfo['environ']: template = "%-25s %s" @@ -315,10 +327,12 @@ def run(pid, verbose=False): def main(): parser = argparse.ArgumentParser( - description="print information about a process") + description="print information about a process" + ) parser.add_argument("pid", type=int, help="process pid", nargs='?') - parser.add_argument('--verbose', '-v', action='store_true', - help="print more info") + parser.add_argument( + '--verbose', '-v', action='store_true', help="print more info" + ) args = parser.parse_args() run(args.pid, args.verbose) diff --git a/scripts/procsmem.py b/scripts/procsmem.py index 574e37041..54e89b05e 100755 --- a/scripts/procsmem.py +++ b/scripts/procsmem.py @@ -96,8 +96,10 @@ def main(): ) print(line) if ad_pids: - print("warning: access denied for %s pids" % (len(ad_pids)), - file=sys.stderr) + print( + "warning: access denied for %s pids" % (len(ad_pids)), + file=sys.stderr, + ) if __name__ == '__main__': diff --git a/scripts/ps.py b/scripts/ps.py index 58a1d8c29..f07b56865 100755 --- a/scripts/ps.py +++ b/scripts/ps.py @@ -41,11 +41,13 @@ def main(): today_day = datetime.date.today() + # fmt: off templ = "%-10s %5s %5s %7s %7s %5s %6s %6s %6s %s" attrs = ['pid', 'memory_percent', 'name', 'cmdline', 'cpu_times', 'create_time', 'memory_info', 'status', 'nice', 'username'] print(templ % ("USER", "PID", "%MEM", "VSZ", "RSS", "NICE", "STATUS", "START", "TIME", "CMDLINE")) + # fmt: on for p in psutil.process_iter(attrs, ad_value=None): if p.info['create_time']: ctime = datetime.datetime.fromtimestamp(p.info['create_time']) @@ -56,8 +58,9 @@ def main(): else: ctime = '' if p.info['cpu_times']: - cputime = time.strftime("%M:%S", - time.localtime(sum(p.info['cpu_times']))) + cputime = time.strftime( + "%M:%S", time.localtime(sum(p.info['cpu_times'])) + ) else: cputime = '' @@ -72,12 +75,21 @@ def main(): if not user: user = '' user = user[:9] - vms = bytes2human(p.info['memory_info'].vms) if \ - p.info['memory_info'] is not None else '' - rss = bytes2human(p.info['memory_info'].rss) if \ - p.info['memory_info'] is not None else '' - memp = round(p.info['memory_percent'], 1) if \ - p.info['memory_percent'] is not None else '' + vms = ( + bytes2human(p.info['memory_info'].vms) + if p.info['memory_info'] is not None + else '' + ) + rss = ( + bytes2human(p.info['memory_info'].rss) + if p.info['memory_info'] is not None + else '' + ) + memp = ( + round(p.info['memory_percent'], 1) + if p.info['memory_percent'] is not None + else '' + ) nice = int(p.info['nice']) if p.info['nice'] else '' if p.info['cmdline']: cmdline = ' '.join(p.info['cmdline']) @@ -95,8 +107,9 @@ def main(): status, ctime, cputime, - cmdline) - print(line[:get_terminal_size()[0]]) + cmdline, + ) + print(line[: get_terminal_size()[0]]) if __name__ == '__main__': diff --git a/scripts/sensors.py b/scripts/sensors.py index a5f9729b4..668cca0a2 100755 --- a/scripts/sensors.py +++ b/scripts/sensors.py @@ -61,23 +61,31 @@ def main(): if name in temps: print(" Temperatures:") for entry in temps[name]: - print(" %-20s %s°C (high=%s°C, critical=%s°C)" % ( - entry.label or name, entry.current, entry.high, - entry.critical)) + s = " %-20s %s°C (high=%s°C, critical=%s°C)" % ( + entry.label or name, + entry.current, + entry.high, + entry.critical, + ) + print(s) # Fans. if name in fans: print(" Fans:") for entry in fans[name]: - print(" %-20s %s RPM" % ( - entry.label or name, entry.current)) + print( + " %-20s %s RPM" + % (entry.label or name, entry.current) + ) # Battery. if battery: print("Battery:") print(" charge: %s%%" % round(battery.percent, 2)) if battery.power_plugged: - print(" status: %s" % ( - "charging" if battery.percent < 100 else "fully charged")) + print( + " status: %s" + % ("charging" if battery.percent < 100 else "fully charged") + ) print(" plugged in: yes") else: print(" left: %s" % secs2hours(battery.secsleft)) diff --git a/scripts/temperatures.py b/scripts/temperatures.py index a211b8873..118ebc265 100755 --- a/scripts/temperatures.py +++ b/scripts/temperatures.py @@ -38,9 +38,13 @@ def main(): for name, entries in temps.items(): print(name) for entry in entries: - print(" %-20s %s °C (high = %s °C, critical = %s °C)" % ( - entry.label or name, entry.current, entry.high, - entry.critical)) + line = " %-20s %s °C (high = %s °C, critical = %s °C)" % ( + entry.label or name, + entry.current, + entry.high, + entry.critical, + ) + print(line) print() diff --git a/scripts/top.py b/scripts/top.py index 675f541ef..c0687ae1f 100755 --- a/scripts/top.py +++ b/scripts/top.py @@ -48,11 +48,7 @@ win = curses.initscr() lineno = 0 -colors_map = dict( - green=3, - red=10, - yellow=4, -) +colors_map = dict(green=3, red=10, yellow=4) def printl(line, color=None, bold=False, highlight=False): @@ -75,6 +71,7 @@ def printl(line, color=None, bold=False, highlight=False): else: lineno += 1 + # --- /curses stuff @@ -85,9 +82,16 @@ def poll(interval): procs_status = {} for p in psutil.process_iter(): try: - p.dict = p.as_dict(['username', 'nice', 'memory_info', - 'memory_percent', 'cpu_percent', - 'cpu_times', 'name', 'status']) + p.dict = p.as_dict([ + 'username', + 'nice', + 'memory_info', + 'memory_percent', + 'cpu_percent', + 'cpu_times', + 'name', + 'status', + ]) try: procs_status[p.dict['status']] += 1 except KeyError: @@ -98,8 +102,9 @@ def poll(interval): procs.append(p) # return processes sorted by CPU percent usage - processes = sorted(procs, key=lambda p: p.dict['cpu_percent'], - reverse=True) + processes = sorted( + procs, key=lambda p: p.dict['cpu_percent'], reverse=True + ) return (processes, procs_status) @@ -131,7 +136,8 @@ def get_dashes(perc): mem = psutil.virtual_memory() dashes, empty_dashes = get_dashes(mem.percent) line = " Mem [%s%s] %5s%% %6s / %s" % ( - dashes, empty_dashes, + dashes, + empty_dashes, mem.percent, bytes2human(mem.used), bytes2human(mem.total), @@ -142,7 +148,8 @@ def get_dashes(perc): swap = psutil.swap_memory() dashes, empty_dashes = get_dashes(swap.percent) line = " Swap [%s%s] %5s%% %6s / %s" % ( - dashes, empty_dashes, + dashes, + empty_dashes, swap.percent, bytes2human(swap.used), bytes2human(swap.total), @@ -157,11 +164,16 @@ def get_dashes(perc): st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1) printl(" Processes: %s (%s)" % (num_procs, ', '.join(st))) # load average, uptime - uptime = datetime.datetime.now() - \ - datetime.datetime.fromtimestamp(psutil.boot_time()) + uptime = datetime.datetime.now() - datetime.datetime.fromtimestamp( + psutil.boot_time() + ) av1, av2, av3 = psutil.getloadavg() - line = " Load average: %.2f %.2f %.2f Uptime: %s" \ - % (av1, av2, av3, str(uptime).split('.')[0]) + line = " Load average: %.2f %.2f %.2f Uptime: %s" % ( + av1, + av2, + av3, + str(uptime).split('.')[0], + ) printl(line) @@ -170,8 +182,17 @@ def refresh_window(procs, procs_status): curses.endwin() templ = "%-6s %-8s %4s %6s %6s %5s %5s %9s %2s" win.erase() - header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%", - "TIME+", "NAME") + header = templ % ( + "PID", + "USER", + "NI", + "VIRT", + "RES", + "CPU%", + "MEM%", + "TIME+", + "NAME", + ) print_header(procs_status, len(procs)) printl("") printl(header, bold=True, highlight=True) @@ -180,9 +201,11 @@ def refresh_window(procs, procs_status): # is expressed as: "mm:ss.ms" if p.dict['cpu_times'] is not None: ctime = datetime.timedelta(seconds=sum(p.dict['cpu_times'])) - ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60, - str(ctime.seconds % 60).zfill(2), - str(ctime.microseconds)[:2]) + ctime = "%s:%s.%s" % ( + ctime.seconds // 60 % 60, + str(ctime.seconds % 60).zfill(2), + str(ctime.microseconds)[:2], + ) else: ctime = '' if p.dict['memory_percent'] is not None: @@ -192,16 +215,17 @@ def refresh_window(procs, procs_status): if p.dict['cpu_percent'] is None: p.dict['cpu_percent'] = '' username = p.dict['username'][:8] if p.dict['username'] else '' - line = templ % (p.pid, - username, - p.dict['nice'], - bytes2human(getattr(p.dict['memory_info'], 'vms', 0)), - bytes2human(getattr(p.dict['memory_info'], 'rss', 0)), - p.dict['cpu_percent'], - p.dict['memory_percent'], - ctime, - p.dict['name'] or '', - ) + line = templ % ( + p.pid, + username, + p.dict['nice'], + bytes2human(getattr(p.dict['memory_info'], 'vms', 0)), + bytes2human(getattr(p.dict['memory_info'], 'rss', 0)), + p.dict['cpu_percent'], + p.dict['memory_percent'], + ctime, + p.dict['name'] or '', + ) try: printl(line) except curses.error: diff --git a/scripts/who.py b/scripts/who.py index 77c474ff9..64a948107 100755 --- a/scripts/who.py +++ b/scripts/who.py @@ -21,13 +21,14 @@ def main(): users = psutil.users() for user in users: proc_name = psutil.Process(user.pid).name() if user.pid else "" - print("%-12s %-10s %-10s %-14s %s" % ( + line = "%-12s %-10s %-10s %-14s %s" % ( user.name, user.terminal or '-', datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"), "(%s)" % user.host if user.host else "", proc_name, - )) + ) + print(line) if __name__ == '__main__': diff --git a/scripts/winservices.py b/scripts/winservices.py index d9c6a14a9..8d941d532 100755 --- a/scripts/winservices.py +++ b/scripts/winservices.py @@ -44,8 +44,13 @@ def main(): for service in psutil.win_service_iter(): info = service.as_dict() print("%r (%r)" % (info['name'], info['display_name'])) - print("status: %s, start: %s, username: %s, pid: %s" % ( - info['status'], info['start_type'], info['username'], info['pid'])) + s = "status: %s, start: %s, username: %s, pid: %s" % ( + info['status'], + info['start_type'], + info['username'], + info['pid'], + ) + print(s) print("binpath: %s" % info['binpath']) print("") diff --git a/setup.py b/setup.py index 1ab704854..7c59f5645 100755 --- a/setup.py +++ b/setup.py @@ -86,15 +86,17 @@ sources.append('psutil/_psutil_posix.c') -extras_require = {"test": [ - "enum34; python_version <= '3.4'", - "ipaddress; python_version < '3.0'", - "mock; python_version < '3.0'", -]} +extras_require = { + "test": [ + "enum34; python_version <= '3.4'", + "ipaddress; python_version < '3.0'", + "mock; python_version < '3.0'", + ] +} if not PYPY: - extras_require['test'].extend([ - "pywin32; sys.platform == 'win32'", - "wmi; sys.platform == 'win32'"]) + extras_require['test'].extend( + ["pywin32; sys.platform == 'win32'", "wmi; sys.platform == 'win32'"] + ) def get_version(): @@ -131,9 +133,12 @@ def get_version(): def get_long_description(): script = os.path.join(HERE, "scripts", "internal", "convert_readme.py") readme = os.path.join(HERE, 'README.rst') - p = subprocess.Popen([sys.executable, script, readme], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) + p = subprocess.Popen( + [sys.executable, script, readme], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) stdout, stderr = p.communicate() if p.returncode != 0: raise RuntimeError(stderr) @@ -173,7 +178,8 @@ def unix_can_compile(c_code): from distutils.unixccompiler import UnixCCompiler with tempfile.NamedTemporaryFile( - suffix='.c', delete=False, mode="wt") as f: + suffix='.c', delete=False, mode="wt" + ) as f: f.write(c_code) tempdir = tempfile.mkdtemp() @@ -195,6 +201,7 @@ def unix_can_compile(c_code): if WINDOWS: + def get_winver(): maj, min = sys.getwindowsversion()[0:2] return '0x0%s' % ((maj * 100) + min) @@ -219,18 +226,27 @@ def get_winver(): ext = Extension( 'psutil._psutil_windows', sources=( - sources + - ["psutil/_psutil_windows.c"] + - glob.glob("psutil/arch/windows/*.c") + sources + + ["psutil/_psutil_windows.c"] + + glob.glob("psutil/arch/windows/*.c") ), define_macros=macros, libraries=[ - "psapi", "kernel32", "advapi32", "shell32", "netapi32", - "ws2_32", "PowrProf", "pdh", + "psapi", + "kernel32", + "advapi32", + "shell32", + "netapi32", + "ws2_32", + "PowrProf", + "pdh", ], # extra_compile_args=["/W 4"], # extra_link_args=["/DEBUG"], - **py_limited_api # noqa (noqa needed for python 2.7) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on ) elif MACOS: @@ -238,57 +254,76 @@ def get_winver(): ext = Extension( 'psutil._psutil_osx', sources=( - sources + - ["psutil/_psutil_osx.c"] + - glob.glob("psutil/arch/osx/*.c") + sources + + ["psutil/_psutil_osx.c"] + + glob.glob("psutil/arch/osx/*.c") ), define_macros=macros, extra_link_args=[ - '-framework', 'CoreFoundation', '-framework', 'IOKit', + '-framework', + 'CoreFoundation', + '-framework', + 'IOKit', ], - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif FREEBSD: macros.append(("PSUTIL_FREEBSD", 1)) ext = Extension( 'psutil._psutil_bsd', sources=( - sources + - ["psutil/_psutil_bsd.c"] + - glob.glob("psutil/arch/bsd/*.c") + - glob.glob("psutil/arch/freebsd/*.c") + sources + + ["psutil/_psutil_bsd.c"] + + glob.glob("psutil/arch/bsd/*.c") + + glob.glob("psutil/arch/freebsd/*.c") ), define_macros=macros, libraries=["devstat"], - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif OPENBSD: macros.append(("PSUTIL_OPENBSD", 1)) ext = Extension( 'psutil._psutil_bsd', sources=( - sources + - ["psutil/_psutil_bsd.c"] + - glob.glob("psutil/arch/bsd/*.c") + - glob.glob("psutil/arch/openbsd/*.c") + sources + + ["psutil/_psutil_bsd.c"] + + glob.glob("psutil/arch/bsd/*.c") + + glob.glob("psutil/arch/openbsd/*.c") ), define_macros=macros, libraries=["kvm"], - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif NETBSD: macros.append(("PSUTIL_NETBSD", 1)) ext = Extension( 'psutil._psutil_bsd', sources=( - sources + - ["psutil/_psutil_bsd.c"] + - glob.glob("psutil/arch/bsd/*.c") + - glob.glob("psutil/arch/netbsd/*.c") + sources + + ["psutil/_psutil_bsd.c"] + + glob.glob("psutil/arch/bsd/*.c") + + glob.glob("psutil/arch/netbsd/*.c") ), define_macros=macros, libraries=["kvm"], - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif LINUX: # see: https://github.com/giampaolo/psutil/issues/659 @@ -299,38 +334,53 @@ def get_winver(): ext = Extension( 'psutil._psutil_linux', sources=( - sources + - ["psutil/_psutil_linux.c"] + - glob.glob("psutil/arch/linux/*.c") + sources + + ["psutil/_psutil_linux.c"] + + glob.glob("psutil/arch/linux/*.c") ), define_macros=macros, - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif SUNOS: macros.append(("PSUTIL_SUNOS", 1)) ext = Extension( 'psutil._psutil_sunos', - sources=sources + [ + sources=sources + + [ 'psutil/_psutil_sunos.c', 'psutil/arch/solaris/v10/ifaddrs.c', 'psutil/arch/solaris/environ.c', ], define_macros=macros, libraries=['kstat', 'nsl', 'socket'], - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) elif AIX: macros.append(("PSUTIL_AIX", 1)) ext = Extension( 'psutil._psutil_aix', - sources=sources + [ + sources=sources + + [ 'psutil/_psutil_aix.c', 'psutil/arch/aix/net_connections.c', 'psutil/arch/aix/common.c', - 'psutil/arch/aix/ifaddrs.c'], + 'psutil/arch/aix/ifaddrs.c', + ], libraries=['perfstat'], define_macros=macros, - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) else: sys.exit('platform %s is not supported' % sys.platform) @@ -341,8 +391,13 @@ def get_winver(): 'psutil._psutil_posix', define_macros=macros, sources=sources, - **py_limited_api) + # fmt: off + # python 2.7 compatibility requires no comma + **py_limited_api + # fmt: on + ) if SUNOS: + def get_sunos_update(): # See https://serverfault.com/q/524883 # for an explanation of Solaris /etc/release @@ -371,6 +426,7 @@ def get_sunos_update(): cmdclass = {} if py_limited_api: + class bdist_wheel_abi3(bdist_wheel): def get_tag(self): python, abi, plat = bdist_wheel.get_tag(self) @@ -384,9 +440,10 @@ def main(): name='psutil', version=VERSION, cmdclass=cmdclass, - description=__doc__ .replace('\n', ' ').strip() if __doc__ else '', + description=__doc__.replace('\n', ' ').strip() if __doc__ else '', long_description=get_long_description(), long_description_content_type='text/x-rst', + # fmt: off keywords=[ 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty', 'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat', @@ -394,6 +451,7 @@ def main(): 'monitoring', 'ulimit', 'prlimit', 'smem', 'performance', 'metrics', 'agent', 'observability', ], + # fmt: on author='Giampaolo Rodola', author_email='g.rodola@gmail.com', url='https://github.com/giampaolo/psutil', @@ -451,7 +509,8 @@ def main(): if setuptools is not None: kwargs.update( python_requires=( - ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"), + ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + ), extras_require=extras_require, zip_safe=False, ) @@ -461,37 +520,47 @@ def main(): success = True finally: cmd = sys.argv[1] if len(sys.argv) >= 2 else '' - if not success and POSIX and \ - cmd.startswith(("build", "install", "sdist", "bdist", - "develop")): + if ( + not success + and POSIX + and cmd.startswith( + ("build", "install", "sdist", "bdist", "develop") + ) + ): py3 = "3" if PY3 else "" if LINUX: pyimpl = "pypy" if PYPY else "python" if which('dpkg'): - missdeps("sudo apt-get install gcc %s%s-dev" % - (pyimpl, py3)) + missdeps( + "sudo apt-get install gcc %s%s-dev" % (pyimpl, py3) + ) elif which('rpm'): missdeps("sudo yum install gcc %s%s-devel" % (pyimpl, py3)) elif which('apk'): missdeps( - "sudo apk add gcc %s%s-dev musl-dev linux-headers" % ( - pyimpl, py3), + "sudo apk add gcc %s%s-dev musl-dev linux-headers" + % (pyimpl, py3) ) elif MACOS: - print(hilite("XCode (https://developer.apple.com/xcode/) " - "is not installed", color="red"), file=sys.stderr) + msg = ( + "XCode (https://developer.apple.com/xcode/)" + " is not installed" + ) + print(hilite(msg, color="red"), file=sys.stderr) elif FREEBSD: if which('pkg'): missdeps("pkg install gcc python%s" % py3) - elif which('mport'): # MidnightBSD + elif which('mport'): # MidnightBSD missdeps("mport install gcc python%s" % py3) elif OPENBSD: missdeps("pkg_add -v gcc python%s" % py3) elif NETBSD: missdeps("pkgin install gcc python%s" % py3) elif SUNOS: - missdeps("sudo ln -s /usr/bin/gcc /usr/local/bin/cc && " - "pkg install gcc") + missdeps( + "sudo ln -s /usr/bin/gcc /usr/local/bin/cc && " + "pkg install gcc" + ) if __name__ == '__main__': From 89eac06f6dd5e19868c21ff1e881cee919f81adb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 7 Jan 2024 00:22:09 +0100 Subject: [PATCH 06/10] refactor tests + make them more robust --- psutil/_pslinux.py | 6 +----- psutil/tests/test_connections.py | 26 +++++++++++++------------- psutil/tests/test_contracts.py | 7 ++++--- psutil/tests/test_linux.py | 13 +++++-------- psutil/tests/test_process.py | 6 +++++- psutil/tests/test_testutils.py | 2 +- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index e19e37a97..798dd3651 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -2031,11 +2031,7 @@ def memory_full_info(self): if HAS_PROC_SMAPS_ROLLUP: # faster try: uss, pss, swap = self._parse_smaps_rollup() - except (ProcessLookupError, FileNotFoundError) as err: - debug( - "ignore %r for pid %s and retry using /proc/pid/smaps" - % (err, self.pid) - ) + except (ProcessLookupError, FileNotFoundError): uss, pss, swap = self._parse_smaps() else: uss, pss, swap = self._parse_smaps() diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index 5924dbb2c..082bf981a 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -55,7 +55,7 @@ def setUp(self): # Process opens a UNIX socket to /var/log/run. return cons = thisproc.connections(kind='all') - assert not cons, cons + self.assertEqual(cons, []) def tearDown(self): # Make sure we closed all resources. @@ -63,7 +63,7 @@ def tearDown(self): if NETBSD or FREEBSD or (MACOS and not PY3): return cons = thisproc.connections(kind='all') - assert not cons, cons + self.assertEqual(cons, []) def compare_procsys_connections(self, pid, proc_cons, kind='all'): """Given a process PID and its list of connections compare @@ -156,7 +156,7 @@ def test_tcp_v4(self): addr = ("127.0.0.1", 0) with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, ()) self.assertEqual(conn.status, psutil.CONN_LISTEN) @unittest.skipIf(not supports_ipv6(), "IPv6 not supported") @@ -164,14 +164,14 @@ def test_tcp_v6(self): addr = ("::1", 0) with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, ()) self.assertEqual(conn.status, psutil.CONN_LISTEN) def test_udp_v4(self): addr = ("127.0.0.1", 0) with closing(bind_socket(AF_INET, SOCK_DGRAM, addr=addr)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, ()) self.assertEqual(conn.status, psutil.CONN_NONE) @unittest.skipIf(not supports_ipv6(), "IPv6 not supported") @@ -179,7 +179,7 @@ def test_udp_v6(self): addr = ("::1", 0) with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, ()) self.assertEqual(conn.status, psutil.CONN_NONE) @unittest.skipIf(not POSIX, 'POSIX only') @@ -187,7 +187,7 @@ def test_unix_tcp(self): testfn = self.get_testfn() with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, "") self.assertEqual(conn.status, psutil.CONN_NONE) @unittest.skipIf(not POSIX, 'POSIX only') @@ -195,7 +195,7 @@ def test_unix_udp(self): testfn = self.get_testfn() with closing(bind_unix_socket(testfn, type=SOCK_STREAM)) as sock: conn = self.check_socket(sock) - assert not conn.raddr + self.assertEqual(conn.raddr, "") self.assertEqual(conn.status, psutil.CONN_NONE) @@ -210,7 +210,7 @@ class TestConnectedSocket(ConnectionTestCase): @unittest.skipIf(SUNOS, "unreliable on SUONS") def test_tcp(self): addr = ("127.0.0.1", 0) - assert not thisproc.connections(kind='tcp4') + self.assertEqual(thisproc.connections(kind='tcp4'), []) server, client = tcp_socketpair(AF_INET, addr=addr) try: cons = thisproc.connections(kind='tcp4') @@ -233,8 +233,8 @@ def test_unix(self): server, client = unix_socketpair(testfn) try: cons = thisproc.connections(kind='unix') - assert not (cons[0].laddr and cons[0].raddr) - assert not (cons[1].laddr and cons[1].raddr) + assert not (cons[0].laddr and cons[0].raddr), cons + assert not (cons[1].laddr and cons[1].raddr), cons if NETBSD or FREEBSD: # On NetBSD creating a UNIX socket will cause # a UNIX connection to /var/run/log. @@ -313,9 +313,9 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): for kind in all_kinds: cons = proc.connections(kind=kind) if kind in kinds: - assert cons + self.assertNotEqual(cons, []) else: - assert not cons, cons + self.assertEqual(cons, []) # compare against system-wide connections # XXX Solaris can't retrieve system-wide UNIX # sockets. diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index aa5a20abb..9dc54f864 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -371,7 +371,8 @@ def proc_info(pid): def check_exception(exc, proc, name, ppid): tcase.assertEqual(exc.pid, pid) - tcase.assertEqual(exc.name, name) + if exc.name is not None: + tcase.assertEqual(exc.name, name) if isinstance(exc, psutil.ZombieProcess): tcase.assertProcessZombie(proc) if exc.ppid is not None: @@ -552,7 +553,7 @@ def username(self, ret, info): def status(self, ret, info): self.assertIsInstance(ret, str) - assert ret + assert ret, ret self.assertNotEqual(ret, '?') # XXX self.assertIn(ret, VALID_PROC_STATUSES) @@ -706,7 +707,7 @@ def is_running(self, ret, info): def cpu_affinity(self, ret, info): self.assertIsInstance(ret, list) - assert ret != [], ret + self.assertNotEqual(ret, []) cpus = list(range(psutil.cpu_count())) for n in ret: self.assertIsInstance(n, int) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index ada196bcd..0aa04f121 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -845,7 +845,7 @@ def path_exists_mock(path): with mock.patch("os.path.exists", side_effect=path_exists_mock): reload_module(psutil._pslinux) ret = psutil.cpu_freq() - assert ret + assert ret, ret self.assertEqual(ret.max, 0.0) self.assertEqual(ret.min, 0.0) for freq in psutil.cpu_freq(percpu=True): @@ -1972,8 +1972,7 @@ def test_open_files_file_gone(self): 'psutil._pslinux.os.readlink', side_effect=OSError(errno.ENOENT, ""), ) as m: - files = p.open_files() - assert not files + self.assertEqual(p.open_files(), []) assert m.called # also simulate the case where os.readlink() returns EINVAL # in which case psutil is supposed to 'continue' @@ -1997,8 +1996,7 @@ def test_open_files_fd_gone(self): with mock.patch( patch_point, side_effect=IOError(errno.ENOENT, "") ) as m: - files = p.open_files() - assert not files + self.assertEqual(p.open_files(), []) assert m.called def test_open_files_enametoolong(self): @@ -2015,8 +2013,7 @@ def test_open_files_enametoolong(self): patch_point, side_effect=OSError(errno.ENAMETOOLONG, "") ) as m: with mock.patch("psutil._pslinux.debug"): - files = p.open_files() - assert not files + self.assertEqual(p.open_files(), []) assert m.called # --- mocked tests @@ -2250,7 +2247,7 @@ def test_connections_enametoolong(self): ) as m: p = psutil.Process() with mock.patch("psutil._pslinux.debug"): - assert not p.connections() + self.assertEqual(p.connections(), []) assert m.called diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 5ccd43b3f..0418fd54e 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -78,7 +78,11 @@ class TestProcess(PsutilTestCase): def spawn_psproc(self, *args, **kwargs): sproc = self.spawn_testproc(*args, **kwargs) - return psutil.Process(sproc.pid) + try: + return psutil.Process(sproc.pid) + except psutil.NoSuchProcess: + self.assertPidGone(sproc.pid) + raise # --- diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py index 825311064..e92c4e11e 100755 --- a/psutil/tests/test_testutils.py +++ b/psutil/tests/test_testutils.py @@ -318,7 +318,7 @@ def tcp_tcp_socketpair(self): def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() - assert not p.connections(kind='unix') + self.assertEqual(p.connections(kind='unix'), []) name = self.get_testfn() server, client = unix_socketpair(name) try: From 14a33ffe057e02abd1adc44dea20924e9a5e41fd Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 6 Jan 2024 18:24:45 -0500 Subject: [PATCH 07/10] Fix cpu_freq for Apple silicon (#2222) Apple SoC returns empty string after querying the cpu frequency using sysctl, this information however, can still be extracted from the IOKit registry. This PR adds a new method that is specific to Apple ARM architecture. Signed-off-by: Oliver T --- CREDITS | 4 ++ HISTORY.rst | 2 + psutil/arch/osx/cpu.c | 121 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index 1ade9d3ef..59715ac76 100644 --- a/CREDITS +++ b/CREDITS @@ -813,3 +813,7 @@ I: 2156, 2345 N: Lawrence D'Anna W: https://github.com/smoofra I: 2010 + +N: Oliver Tomé +W: https://github.com/snom3ad +I: 2222 diff --git a/HISTORY.rst b/HISTORY.rst index 8467b90e5..e0316f66c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -20,6 +20,8 @@ - 2340_, [NetBSD]: if process is terminated, `Process.cwd()`_ will return an empty string instead of raising `NoSuchProcess`_. - 2345_, [Linux]: fix compilation on older compiler missing DUPLEX_UNKNOWN +- 2222_, [macOS]: `cpu_freq()` now returns fixed values for `min` and `max` + frequencies in all Apple Silicon chips. 5.9.7 ===== diff --git a/psutil/arch/osx/cpu.c b/psutil/arch/osx/cpu.c index a1ba11429..4196083ec 100644 --- a/psutil/arch/osx/cpu.c +++ b/psutil/arch/osx/cpu.c @@ -26,6 +26,10 @@ For reference, here's the git history with original implementations: #include #include #include +#if defined(__arm64__) || defined(__aarch64__) +#include +#include +#endif #include "../../_psutil_common.h" #include "../../_psutil_posix.h" @@ -109,7 +113,122 @@ psutil_cpu_stats(PyObject *self, PyObject *args) { ); } +#if defined(__arm64__) || defined(__aarch64__) +PyObject * +psutil_cpu_freq(PyObject *self, PyObject *args) { + uint32_t min; + uint32_t curr; + uint32_t pMin; + uint32_t eMin; + uint32_t max; + kern_return_t status; + CFDictionaryRef matching = NULL; + CFTypeRef pCoreRef = NULL; + CFTypeRef eCoreRef = NULL; + io_iterator_t iter = 0; + io_registry_entry_t entry = 0; + io_name_t name; + + matching = IOServiceMatching("AppleARMIODevice"); + if (matching == 0) { + return PyErr_Format( + PyExc_RuntimeError, + "IOServiceMatching call failed, 'AppleARMIODevice' not found" + ); + } + + status = IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iter); + if (status != KERN_SUCCESS) { + PyErr_Format( + PyExc_RuntimeError, "IOServiceGetMatchingServices call failed" + ); + goto error; + } + + while ((entry = IOIteratorNext(iter)) != 0) { + status = IORegistryEntryGetName(entry, name); + if (status != KERN_SUCCESS) { + IOObjectRelease(entry); + continue; + } + if (strcmp(name, "pmgr") == 0) { + break; + } + IOObjectRelease(entry); + } + if (entry == 0) { + PyErr_Format( + PyExc_RuntimeError, + "'pmgr' entry was not found in AppleARMIODevice service" + ); + goto error; + } + + pCoreRef = IORegistryEntryCreateCFProperty( + entry, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, 0); + if (pCoreRef == NULL) { + PyErr_Format( + PyExc_RuntimeError, "'voltage-states5-sram' property not found"); + goto error; + } + + eCoreRef = IORegistryEntryCreateCFProperty( + entry, CFSTR("voltage-states1-sram"), kCFAllocatorDefault, 0); + if (eCoreRef == NULL) { + PyErr_Format( + PyExc_RuntimeError, "'voltage-states1-sram' property not found"); + goto error; + } + + size_t pCoreLength = CFDataGetLength(pCoreRef); + size_t eCoreLength = CFDataGetLength(eCoreRef); + if (pCoreLength < 8) { + PyErr_Format( + PyExc_RuntimeError, + "expected 'voltage-states5-sram' buffer to have at least size 8" + ); + goto error; + } + if (eCoreLength < 4) { + PyErr_Format( + PyExc_RuntimeError, + "expected 'voltage-states1-sram' buffer to have at least size 4" + ); + goto error; + } + + CFDataGetBytes(pCoreRef, CFRangeMake(0, 4), (UInt8 *) &pMin); + CFDataGetBytes(eCoreRef, CFRangeMake(0, 4), (UInt8 *) &eMin); + CFDataGetBytes(pCoreRef, CFRangeMake(pCoreLength - 8, 4), (UInt8 *) &max); + + min = pMin < eMin ? pMin : eMin; + curr = max; + + CFRelease(pCoreRef); + CFRelease(eCoreRef); + IOObjectRelease(iter); + IOObjectRelease(entry); + + return Py_BuildValue( + "IKK", + curr / 1000 / 1000, + min / 1000 / 1000, + max / 1000 / 1000 + ); + +error: + if (pCoreRef != NULL) + CFRelease(pCoreRef); + if (eCoreRef != NULL) + CFRelease(eCoreRef); + if (iter != 0) + IOObjectRelease(iter); + if (entry != 0) + IOObjectRelease(entry); + return NULL; +} +#else PyObject * psutil_cpu_freq(PyObject *self, PyObject *args) { unsigned int curr; @@ -138,7 +257,7 @@ psutil_cpu_freq(PyObject *self, PyObject *args) { min / 1000 / 1000, max / 1000 / 1000); } - +#endif PyObject * psutil_per_cpu_times(PyObject *self, PyObject *args) { From 82a43754a7f4898e7a3a2f9721ba32bf7c00a925 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 7 Jan 2024 02:09:37 +0100 Subject: [PATCH 08/10] improve tests reliability --- MANIFEST.in | 1 + Makefile | 4 + psutil/tests/__init__.py | 17 +- psutil/tests/test_connections.py | 59 ++-- psutil/tests/test_contracts.py | 437 ----------------------------- psutil/tests/test_process.py | 18 +- psutil/tests/test_process_all.py | 464 +++++++++++++++++++++++++++++++ psutil/tests/test_testutils.py | 13 +- scripts/internal/winmake.py | 27 +- 9 files changed, 541 insertions(+), 499 deletions(-) create mode 100755 psutil/tests/test_process_all.py diff --git a/MANIFEST.in b/MANIFEST.in index 14ede9455..bb60aa849 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -164,6 +164,7 @@ include psutil/tests/test_misc.py include psutil/tests/test_osx.py include psutil/tests/test_posix.py include psutil/tests/test_process.py +include psutil/tests/test_process_all.py include psutil/tests/test_sunos.py include psutil/tests/test_system.py include psutil/tests/test_testutils.py diff --git a/Makefile b/Makefile index 6b6c3f32e..34279c90a 100644 --- a/Makefile +++ b/Makefile @@ -140,6 +140,10 @@ test-process: ## Run process-related API tests. ${MAKE} build $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_process.py +test-process-all: ## Run tests which iterate over all process PIDs. + ${MAKE} build + $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_process_all.py + test-system: ## Run system-related API tests. ${MAKE} build $(TEST_PREFIX) $(PYTHON) $(TSCRIPT) $(ARGS) psutil/tests/test_system.py diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 05f0d63b8..df30e3069 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -47,6 +47,7 @@ from psutil import SUNOS from psutil import WINDOWS from psutil._common import bytes2human +from psutil._common import debug from psutil._common import memoize from psutil._common import print_color from psutil._common import supports_ipv6 @@ -105,7 +106,7 @@ # sync primitives 'call_until', 'wait_for_pid', 'wait_for_file', # network - 'check_net_address', + 'check_net_address', 'filter_proc_connections', 'get_free_port', 'bind_socket', 'bind_unix_socket', 'tcp_socketpair', 'unix_socketpair', 'create_sockets', # compat @@ -1871,6 +1872,20 @@ def check_status(conn): check_status(conn) +def filter_proc_connections(cons): + """Our process may start with some open UNIX sockets which are not + initialized by us, invalidating unit tests. + """ + new = [] + for conn in cons: + if POSIX and conn.family == socket.AF_UNIX: + if MACOS and "/syslog" in conn.raddr: + debug("skipping %s" % str(conn)) + continue + new.append(conn) + return new + + # =================================================================== # --- compatibility # =================================================================== diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py index 082bf981a..e261fc0f9 100755 --- a/psutil/tests/test_connections.py +++ b/psutil/tests/test_connections.py @@ -35,6 +35,7 @@ from psutil.tests import bind_unix_socket from psutil.tests import check_connection_ntuple from psutil.tests import create_sockets +from psutil.tests import filter_proc_connections from psutil.tests import reap_children from psutil.tests import retry_on_failure from psutil.tests import serialrun @@ -44,26 +45,24 @@ from psutil.tests import wait_for_file -thisproc = psutil.Process() SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object()) +def this_proc_connections(kind): + cons = psutil.Process().connections(kind=kind) + if kind in ("all", "unix"): + return filter_proc_connections(cons) + return cons + + @serialrun class ConnectionTestCase(PsutilTestCase): def setUp(self): - if NETBSD or FREEBSD or (MACOS and not PY3): - # Process opens a UNIX socket to /var/log/run. - return - cons = thisproc.connections(kind='all') - self.assertEqual(cons, []) + self.assertEqual(this_proc_connections(kind='all'), []) def tearDown(self): # Make sure we closed all resources. - # Some BSDs open a UNIX socket to /var/log/run. - if NETBSD or FREEBSD or (MACOS and not PY3): - return - cons = thisproc.connections(kind='all') - self.assertEqual(cons, []) + self.assertEqual(this_proc_connections(kind='all'), []) def compare_procsys_connections(self, pid, proc_cons, kind='all'): """Given a process PID and its list of connections compare @@ -95,11 +94,11 @@ def test_system(self): def test_process(self): with create_sockets(): - for conn in psutil.Process().connections(kind='all'): + for conn in this_proc_connections(kind='all'): check_connection_ntuple(conn) def test_invalid_kind(self): - self.assertRaises(ValueError, thisproc.connections, kind='???') + self.assertRaises(ValueError, this_proc_connections, kind='???') self.assertRaises(ValueError, psutil.net_connections, kind='???') @@ -108,7 +107,7 @@ class TestUnconnectedSockets(ConnectionTestCase): """Tests sockets which are open but not connected to anything.""" def get_conn_from_sock(self, sock): - cons = thisproc.connections(kind='all') + cons = this_proc_connections(kind='all') smap = dict([(c.fd, c) for c in cons]) if NETBSD or FREEBSD: # NetBSD opens a UNIX socket to /var/log/run @@ -148,7 +147,7 @@ def check_socket(self, sock): # XXX Solaris can't retrieve system-wide UNIX sockets if sock.family == AF_UNIX and HAS_CONNECTIONS_UNIX: - cons = thisproc.connections(kind='all') + cons = this_proc_connections(kind='all') self.compare_procsys_connections(os.getpid(), cons, kind='all') return conn @@ -210,17 +209,17 @@ class TestConnectedSocket(ConnectionTestCase): @unittest.skipIf(SUNOS, "unreliable on SUONS") def test_tcp(self): addr = ("127.0.0.1", 0) - self.assertEqual(thisproc.connections(kind='tcp4'), []) + self.assertEqual(this_proc_connections(kind='tcp4'), []) server, client = tcp_socketpair(AF_INET, addr=addr) try: - cons = thisproc.connections(kind='tcp4') + cons = this_proc_connections(kind='tcp4') self.assertEqual(len(cons), 2) self.assertEqual(cons[0].status, psutil.CONN_ESTABLISHED) self.assertEqual(cons[1].status, psutil.CONN_ESTABLISHED) # May not be fast enough to change state so it stays # commenteed. # client.close() - # cons = thisproc.connections(kind='all') + # cons = this_proc_connections(kind='all') # self.assertEqual(len(cons), 1) # self.assertEqual(cons[0].status, psutil.CONN_CLOSE_WAIT) finally: @@ -232,7 +231,7 @@ def test_unix(self): testfn = self.get_testfn() server, client = unix_socketpair(testfn) try: - cons = thisproc.connections(kind='unix') + cons = this_proc_connections(kind='unix') assert not (cons[0].laddr and cons[0].raddr), cons assert not (cons[1].laddr and cons[1].raddr), cons if NETBSD or FREEBSD: @@ -258,7 +257,7 @@ def test_unix(self): class TestFilters(ConnectionTestCase): def test_filters(self): def check(kind, families, types): - for conn in thisproc.connections(kind=kind): + for conn in this_proc_connections(kind=kind): self.assertIn(conn.family, families) self.assertIn(conn.type, types) if not SKIP_SYSCONS: @@ -373,7 +372,7 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): tcp6_addr = None udp6_addr = None - for p in thisproc.children(): + for p in psutil.Process().children(): cons = p.connections() self.assertEqual(len(cons), 1) for conn in cons: @@ -429,48 +428,48 @@ def check_conn(proc, conn, family, type, laddr, raddr, status, kinds): def test_count(self): with create_sockets(): # tcp - cons = thisproc.connections(kind='tcp') + cons = this_proc_connections(kind='tcp') self.assertEqual(len(cons), 2 if supports_ipv6() else 1) for conn in cons: self.assertIn(conn.family, (AF_INET, AF_INET6)) self.assertEqual(conn.type, SOCK_STREAM) # tcp4 - cons = thisproc.connections(kind='tcp4') + cons = this_proc_connections(kind='tcp4') self.assertEqual(len(cons), 1) self.assertEqual(cons[0].family, AF_INET) self.assertEqual(cons[0].type, SOCK_STREAM) # tcp6 if supports_ipv6(): - cons = thisproc.connections(kind='tcp6') + cons = this_proc_connections(kind='tcp6') self.assertEqual(len(cons), 1) self.assertEqual(cons[0].family, AF_INET6) self.assertEqual(cons[0].type, SOCK_STREAM) # udp - cons = thisproc.connections(kind='udp') + cons = this_proc_connections(kind='udp') self.assertEqual(len(cons), 2 if supports_ipv6() else 1) for conn in cons: self.assertIn(conn.family, (AF_INET, AF_INET6)) self.assertEqual(conn.type, SOCK_DGRAM) # udp4 - cons = thisproc.connections(kind='udp4') + cons = this_proc_connections(kind='udp4') self.assertEqual(len(cons), 1) self.assertEqual(cons[0].family, AF_INET) self.assertEqual(cons[0].type, SOCK_DGRAM) # udp6 if supports_ipv6(): - cons = thisproc.connections(kind='udp6') + cons = this_proc_connections(kind='udp6') self.assertEqual(len(cons), 1) self.assertEqual(cons[0].family, AF_INET6) self.assertEqual(cons[0].type, SOCK_DGRAM) # inet - cons = thisproc.connections(kind='inet') + cons = this_proc_connections(kind='inet') self.assertEqual(len(cons), 4 if supports_ipv6() else 2) for conn in cons: self.assertIn(conn.family, (AF_INET, AF_INET6)) self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM)) # inet6 if supports_ipv6(): - cons = thisproc.connections(kind='inet6') + cons = this_proc_connections(kind='inet6') self.assertEqual(len(cons), 2) for conn in cons: self.assertEqual(conn.family, AF_INET6) @@ -478,7 +477,7 @@ def test_count(self): # Skipped on BSD becayse by default the Python process # creates a UNIX socket to '/var/run/log'. if HAS_CONNECTIONS_UNIX and not (FREEBSD or NETBSD): - cons = thisproc.connections(kind='unix') + cons = this_proc_connections(kind='unix') self.assertEqual(len(cons), 3) for conn in cons: self.assertEqual(conn.family, AF_UNIX) diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py index 9dc54f864..4736f5f1a 100755 --- a/psutil/tests/test_contracts.py +++ b/psutil/tests/test_contracts.py @@ -9,34 +9,21 @@ Some of these are duplicates of tests test_system.py and test_process.py. """ -import errno -import multiprocessing -import os import platform import signal -import stat -import time -import traceback import unittest import psutil from psutil import AIX -from psutil import BSD from psutil import FREEBSD from psutil import LINUX from psutil import MACOS from psutil import NETBSD from psutil import OPENBSD -from psutil import OSX from psutil import POSIX from psutil import SUNOS from psutil import WINDOWS -from psutil._compat import PY3 -from psutil._compat import FileNotFoundError from psutil._compat import long -from psutil._compat import range -from psutil._compat import unicode -from psutil.tests import CI_TESTING from psutil.tests import GITHUB_ACTIONS from psutil.tests import HAS_CPU_FREQ from psutil.tests import HAS_NET_IO_COUNTERS @@ -44,16 +31,11 @@ from psutil.tests import HAS_SENSORS_TEMPERATURES from psutil.tests import PYPY from psutil.tests import SKIP_SYSCONS -from psutil.tests import VALID_PROC_STATUSES from psutil.tests import PsutilTestCase -from psutil.tests import check_connection_ntuple from psutil.tests import create_sockets from psutil.tests import enum from psutil.tests import is_namedtuple -from psutil.tests import is_win_secure_system_proc from psutil.tests import kernel_version -from psutil.tests import process_namespace -from psutil.tests import serialrun # =================================================================== @@ -361,425 +343,6 @@ def test_negative_signal(self): self.assertIsInstance(code, int) -# =================================================================== -# --- Featch all processes test -# =================================================================== - - -def proc_info(pid): - tcase = PsutilTestCase() - - def check_exception(exc, proc, name, ppid): - tcase.assertEqual(exc.pid, pid) - if exc.name is not None: - tcase.assertEqual(exc.name, name) - if isinstance(exc, psutil.ZombieProcess): - tcase.assertProcessZombie(proc) - if exc.ppid is not None: - tcase.assertGreaterEqual(exc.ppid, 0) - tcase.assertEqual(exc.ppid, ppid) - elif isinstance(exc, psutil.NoSuchProcess): - tcase.assertProcessGone(proc) - str(exc) - repr(exc) - - def do_wait(): - if pid != 0: - try: - proc.wait(0) - except psutil.Error as exc: - check_exception(exc, proc, name, ppid) - - try: - proc = psutil.Process(pid) - except psutil.NoSuchProcess: - tcase.assertPidGone(pid) - return {} - try: - d = proc.as_dict(['ppid', 'name']) - except psutil.NoSuchProcess: - tcase.assertProcessGone(proc) - else: - name, ppid = d['name'], d['ppid'] - info = {'pid': proc.pid} - ns = process_namespace(proc) - # We don't use oneshot() because in order not to fool - # check_exception() in case of NSP. - for fun, fun_name in ns.iter(ns.getters, clear_cache=False): - try: - info[fun_name] = fun() - except psutil.Error as exc: - check_exception(exc, proc, name, ppid) - continue - do_wait() - return info - - -@serialrun -class TestFetchAllProcesses(PsutilTestCase): - """Test which iterates over all running processes and performs - some sanity checks against Process API's returned values. - Uses a process pool to get info about all processes. - """ - - use_proc_pool = 0 - - def setUp(self): - # Using a pool in a CI env may result in deadlock, see: - # https://github.com/giampaolo/psutil/issues/2104 - if self.use_proc_pool: - self.pool = multiprocessing.Pool() - - def tearDown(self): - if self.use_proc_pool: - self.pool.terminate() - self.pool.join() - - def iter_proc_info(self): - # Fixes "can't pickle : it's not the - # same object as test_contracts.proc_info". - from psutil.tests.test_contracts import proc_info - - if self.use_proc_pool: - return self.pool.imap_unordered(proc_info, psutil.pids()) - else: - ls = [] - for pid in psutil.pids(): - ls.append(proc_info(pid)) - return ls - - def test_all(self): - failures = [] - for info in self.iter_proc_info(): - for name, value in info.items(): - meth = getattr(self, name) - try: - meth(value, info) - except Exception: # noqa: BLE001 - s = '\n' + '=' * 70 + '\n' - s += "FAIL: name=test_%s, pid=%s, ret=%s\ninfo=%s\n" % ( - name, - info['pid'], - repr(value), - info, - ) - s += '-' * 70 - s += "\n%s" % traceback.format_exc() - s = "\n".join((" " * 4) + i for i in s.splitlines()) + "\n" - failures.append(s) - else: - if value not in (0, 0.0, [], None, '', {}): - assert value, value - if failures: - raise self.fail(''.join(failures)) - - def cmdline(self, ret, info): - self.assertIsInstance(ret, list) - for part in ret: - self.assertIsInstance(part, str) - - def exe(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) - self.assertEqual(ret.strip(), ret) - if ret: - if WINDOWS and not ret.endswith('.exe'): - return # May be "Registry", "MemCompression", ... - assert os.path.isabs(ret), ret - # Note: os.stat() may return False even if the file is there - # hence we skip the test, see: - # http://stackoverflow.com/questions/3112546/os-path-exists-lies - if POSIX and os.path.isfile(ret): - if hasattr(os, 'access') and hasattr(os, "X_OK"): - # XXX: may fail on MACOS - try: - assert os.access(ret, os.X_OK) - except AssertionError: - if os.path.exists(ret) and not CI_TESTING: - raise - - def pid(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) - - def ppid(self, ret, info): - self.assertIsInstance(ret, (int, long)) - self.assertGreaterEqual(ret, 0) - proc_info(ret) - - def name(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) - if WINDOWS and not ret and is_win_secure_system_proc(info['pid']): - # https://github.com/giampaolo/psutil/issues/2338 - return - # on AIX, "" processes don't have names - if not AIX: - assert ret, repr(ret) - - def create_time(self, ret, info): - self.assertIsInstance(ret, float) - try: - self.assertGreaterEqual(ret, 0) - except AssertionError: - # XXX - if OPENBSD and info['status'] == psutil.STATUS_ZOMBIE: - pass - else: - raise - # this can't be taken for granted on all platforms - # self.assertGreaterEqual(ret, psutil.boot_time()) - # make sure returned value can be pretty printed - # with strftime - time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret)) - - def uids(self, ret, info): - assert is_namedtuple(ret) - for uid in ret: - self.assertIsInstance(uid, int) - self.assertGreaterEqual(uid, 0) - - def gids(self, ret, info): - assert is_namedtuple(ret) - # note: testing all gids as above seems not to be reliable for - # gid == 30 (nodoby); not sure why. - for gid in ret: - self.assertIsInstance(gid, int) - if not MACOS and not NETBSD: - self.assertGreaterEqual(gid, 0) - - def username(self, ret, info): - self.assertIsInstance(ret, str) - self.assertEqual(ret.strip(), ret) - assert ret.strip() - - def status(self, ret, info): - self.assertIsInstance(ret, str) - assert ret, ret - self.assertNotEqual(ret, '?') # XXX - self.assertIn(ret, VALID_PROC_STATUSES) - - def io_counters(self, ret, info): - assert is_namedtuple(ret) - for field in ret: - self.assertIsInstance(field, (int, long)) - if field != -1: - self.assertGreaterEqual(field, 0) - - def ionice(self, ret, info): - if LINUX: - self.assertIsInstance(ret.ioclass, int) - self.assertIsInstance(ret.value, int) - self.assertGreaterEqual(ret.ioclass, 0) - self.assertGreaterEqual(ret.value, 0) - else: # Windows, Cygwin - choices = [ - psutil.IOPRIO_VERYLOW, - psutil.IOPRIO_LOW, - psutil.IOPRIO_NORMAL, - psutil.IOPRIO_HIGH, - ] - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) - self.assertIn(ret, choices) - - def num_threads(self, ret, info): - self.assertIsInstance(ret, int) - if WINDOWS and ret == 0 and is_win_secure_system_proc(info['pid']): - # https://github.com/giampaolo/psutil/issues/2338 - return - self.assertGreaterEqual(ret, 1) - - def threads(self, ret, info): - self.assertIsInstance(ret, list) - for t in ret: - assert is_namedtuple(t) - self.assertGreaterEqual(t.id, 0) - self.assertGreaterEqual(t.user_time, 0) - self.assertGreaterEqual(t.system_time, 0) - for field in t: - self.assertIsInstance(field, (int, float)) - - def cpu_times(self, ret, info): - assert is_namedtuple(ret) - for n in ret: - self.assertIsInstance(n, float) - self.assertGreaterEqual(n, 0) - # TODO: check ntuple fields - - def cpu_percent(self, ret, info): - self.assertIsInstance(ret, float) - assert 0.0 <= ret <= 100.0, ret - - def cpu_num(self, ret, info): - self.assertIsInstance(ret, int) - if FREEBSD and ret == -1: - return - self.assertGreaterEqual(ret, 0) - if psutil.cpu_count() == 1: - self.assertEqual(ret, 0) - self.assertIn(ret, list(range(psutil.cpu_count()))) - - def memory_info(self, ret, info): - assert is_namedtuple(ret) - for value in ret: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) - if WINDOWS: - self.assertGreaterEqual(ret.peak_wset, ret.wset) - self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool) - self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool) - self.assertGreaterEqual(ret.peak_pagefile, ret.pagefile) - - def memory_full_info(self, ret, info): - assert is_namedtuple(ret) - total = psutil.virtual_memory().total - for name in ret._fields: - value = getattr(ret, name) - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0, msg=(name, value)) - if LINUX or OSX and name in ('vms', 'data'): - # On Linux there are processes (e.g. 'goa-daemon') whose - # VMS is incredibly high for some reason. - continue - self.assertLessEqual(value, total, msg=(name, value, total)) - - if LINUX: - self.assertGreaterEqual(ret.pss, ret.uss) - - def open_files(self, ret, info): - self.assertIsInstance(ret, list) - for f in ret: - self.assertIsInstance(f.fd, int) - self.assertIsInstance(f.path, str) - self.assertEqual(f.path.strip(), f.path) - if WINDOWS: - self.assertEqual(f.fd, -1) - elif LINUX: - self.assertIsInstance(f.position, int) - self.assertIsInstance(f.mode, str) - self.assertIsInstance(f.flags, int) - self.assertGreaterEqual(f.position, 0) - self.assertIn(f.mode, ('r', 'w', 'a', 'r+', 'a+')) - self.assertGreater(f.flags, 0) - elif BSD and not f.path: - # XXX see: https://github.com/giampaolo/psutil/issues/595 - continue - assert os.path.isabs(f.path), f - try: - st = os.stat(f.path) - except FileNotFoundError: - pass - else: - assert stat.S_ISREG(st.st_mode), f - - def num_fds(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) - - def connections(self, ret, info): - with create_sockets(): - self.assertEqual(len(ret), len(set(ret))) - for conn in ret: - assert is_namedtuple(conn) - check_connection_ntuple(conn) - - def cwd(self, ret, info): - self.assertIsInstance(ret, (str, unicode)) - self.assertEqual(ret.strip(), ret) - if ret: - assert os.path.isabs(ret), ret - try: - st = os.stat(ret) - except OSError as err: - if WINDOWS and psutil._psplatform.is_permission_err(err): - pass - # directory has been removed in mean time - elif err.errno != errno.ENOENT: - raise - else: - assert stat.S_ISDIR(st.st_mode) - - def memory_percent(self, ret, info): - self.assertIsInstance(ret, float) - assert 0 <= ret <= 100, ret - - def is_running(self, ret, info): - self.assertIsInstance(ret, bool) - - def cpu_affinity(self, ret, info): - self.assertIsInstance(ret, list) - self.assertNotEqual(ret, []) - cpus = list(range(psutil.cpu_count())) - for n in ret: - self.assertIsInstance(n, int) - self.assertIn(n, cpus) - - def terminal(self, ret, info): - self.assertIsInstance(ret, (str, type(None))) - if ret is not None: - assert os.path.isabs(ret), ret - assert os.path.exists(ret), ret - - def memory_maps(self, ret, info): - for nt in ret: - self.assertIsInstance(nt.addr, str) - self.assertIsInstance(nt.perms, str) - self.assertIsInstance(nt.path, str) - for fname in nt._fields: - value = getattr(nt, fname) - if fname == 'path': - if not value.startswith(("[", "anon_inode:")): - assert os.path.isabs(nt.path), nt.path - # commented as on Linux we might get - # '/foo/bar (deleted)' - # assert os.path.exists(nt.path), nt.path - elif fname == 'addr': - assert value, repr(value) - elif fname == 'perms': - if not WINDOWS: - assert value, repr(value) - else: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) - - def num_handles(self, ret, info): - self.assertIsInstance(ret, int) - self.assertGreaterEqual(ret, 0) - - def nice(self, ret, info): - self.assertIsInstance(ret, int) - if POSIX: - assert -20 <= ret <= 20, ret - else: - priorities = [ - getattr(psutil, x) - for x in dir(psutil) - if x.endswith('_PRIORITY_CLASS') - ] - self.assertIn(ret, priorities) - if PY3: - self.assertIsInstance(ret, enum.IntEnum) - else: - self.assertIsInstance(ret, int) - - def num_ctx_switches(self, ret, info): - assert is_namedtuple(ret) - for value in ret: - self.assertIsInstance(value, (int, long)) - self.assertGreaterEqual(value, 0) - - def rlimit(self, ret, info): - self.assertIsInstance(ret, tuple) - self.assertEqual(len(ret), 2) - self.assertGreaterEqual(ret[0], -1) - self.assertGreaterEqual(ret[1], -1) - - def environ(self, ret, info): - self.assertIsInstance(ret, dict) - for k, v in ret.items(): - self.assertIsInstance(k, str) - self.assertIsInstance(v, str) - - if __name__ == '__main__': from psutil.tests.runner import run_from_name diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index 0418fd54e..f03a22da0 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -746,9 +746,9 @@ def test_cmdline(self): @unittest.skipIf(PYPY, "broken on PYPY") def test_long_cmdline(self): - testfn = self.get_testfn() - create_exe(testfn) - cmdline = [testfn] + (["0123456789"] * 20) + cmdline = [PYTHON_EXE] + cmdline.extend(["-v"] * 50) + cmdline.extend(["-c", "time.sleep(10)"]) p = self.spawn_psproc(cmdline) if OPENBSD: # XXX: for some reason the test process may turn into a @@ -770,7 +770,7 @@ def test_name(self): def test_long_name(self): testfn = self.get_testfn(suffix="0123456789" * 2) create_exe(testfn) - cmdline = [testfn] + (["0123456789"] * 20) + cmdline = [testfn, "-c", "time.sleep(10)"] p = self.spawn_psproc(cmdline) if OPENBSD: # XXX: for some reason the test process may turn into a @@ -800,15 +800,7 @@ def test_prog_w_funky_name(self): # https://github.com/giampaolo/psutil/issues/628 funky_path = self.get_testfn(suffix='foo bar )') create_exe(funky_path) - cmdline = [ - funky_path, - "-c", - "import time; [time.sleep(0.01) for x in range(3000)];arg1", - "arg2", - "", - "arg3", - "", - ] + cmdline = [funky_path, "-c", "time.sleep(10)"] p = self.spawn_psproc(cmdline) self.assertEqual(p.cmdline(), cmdline) self.assertEqual(p.name(), os.path.basename(funky_path)) diff --git a/psutil/tests/test_process_all.py b/psutil/tests/test_process_all.py new file mode 100755 index 000000000..ee1a9b58d --- /dev/null +++ b/psutil/tests/test_process_all.py @@ -0,0 +1,464 @@ +#!/usr/bin/env python3 + +# 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. + +"""Iterate over all process PIDs and for each one of them invoke and +test all psutil.Process() methods. +""" + +import enum +import errno +import multiprocessing +import os +import stat +import time +import traceback + +import psutil +from psutil import AIX +from psutil import BSD +from psutil import FREEBSD +from psutil import LINUX +from psutil import MACOS +from psutil import NETBSD +from psutil import OPENBSD +from psutil import OSX +from psutil import POSIX +from psutil import WINDOWS +from psutil._compat import PY3 +from psutil._compat import long +from psutil._compat import unicode +from psutil.tests import CI_TESTING +from psutil.tests import VALID_PROC_STATUSES +from psutil.tests import PsutilTestCase +from psutil.tests import check_connection_ntuple +from psutil.tests import create_sockets +from psutil.tests import is_namedtuple +from psutil.tests import is_win_secure_system_proc +from psutil.tests import process_namespace +from psutil.tests import serialrun + + +# Cuts the time in half, but (e.g.) on macOS the process pool stays +# alive after join() (multiprocessing bug?), messing up other tests. +USE_PROC_POOL = LINUX and not CI_TESTING + + +def proc_info(pid): + tcase = PsutilTestCase() + + def check_exception(exc, proc, name, ppid): + tcase.assertEqual(exc.pid, pid) + if exc.name is not None: + tcase.assertEqual(exc.name, name) + if isinstance(exc, psutil.ZombieProcess): + tcase.assertProcessZombie(proc) + if exc.ppid is not None: + tcase.assertGreaterEqual(exc.ppid, 0) + tcase.assertEqual(exc.ppid, ppid) + elif isinstance(exc, psutil.NoSuchProcess): + tcase.assertProcessGone(proc) + str(exc) + repr(exc) + + def do_wait(): + if pid != 0: + try: + proc.wait(0) + except psutil.Error as exc: + check_exception(exc, proc, name, ppid) + + try: + proc = psutil.Process(pid) + except psutil.NoSuchProcess: + tcase.assertPidGone(pid) + return {} + try: + d = proc.as_dict(['ppid', 'name']) + except psutil.NoSuchProcess: + tcase.assertProcessGone(proc) + else: + name, ppid = d['name'], d['ppid'] + info = {'pid': proc.pid} + ns = process_namespace(proc) + # We don't use oneshot() because in order not to fool + # check_exception() in case of NSP. + for fun, fun_name in ns.iter(ns.getters, clear_cache=False): + try: + info[fun_name] = fun() + except psutil.Error as exc: + check_exception(exc, proc, name, ppid) + continue + do_wait() + return info + + +@serialrun +class TestFetchAllProcesses(PsutilTestCase): + """Test which iterates over all running processes and performs + some sanity checks against Process API's returned values. + Uses a process pool to get info about all processes. + """ + + def setUp(self): + # Using a pool in a CI env may result in deadlock, see: + # https://github.com/giampaolo/psutil/issues/2104 + if USE_PROC_POOL: + self.pool = multiprocessing.Pool() + + def tearDown(self): + if USE_PROC_POOL: + self.pool.terminate() + self.pool.join() + + def iter_proc_info(self): + # Fixes "can't pickle : it's not the + # same object as test_process_all.proc_info". + from psutil.tests.test_process_all import proc_info + + if USE_PROC_POOL: + return self.pool.imap_unordered(proc_info, psutil.pids()) + else: + ls = [] + for pid in psutil.pids(): + ls.append(proc_info(pid)) + return ls + + def test_all(self): + failures = [] + for info in self.iter_proc_info(): + for name, value in info.items(): + meth = getattr(self, name) + try: + meth(value, info) + except Exception: # noqa: BLE001 + s = '\n' + '=' * 70 + '\n' + s += "FAIL: name=test_%s, pid=%s, ret=%s\ninfo=%s\n" % ( + name, + info['pid'], + repr(value), + info, + ) + s += '-' * 70 + s += "\n%s" % traceback.format_exc() + s = "\n".join((" " * 4) + i for i in s.splitlines()) + "\n" + failures.append(s) + else: + if value not in (0, 0.0, [], None, '', {}): + assert value, value + if failures: + raise self.fail(''.join(failures)) + + def cmdline(self, ret, info): + self.assertIsInstance(ret, list) + for part in ret: + self.assertIsInstance(part, str) + + def exe(self, ret, info): + self.assertIsInstance(ret, (str, unicode)) + self.assertEqual(ret.strip(), ret) + if ret: + if WINDOWS and not ret.endswith('.exe'): + return # May be "Registry", "MemCompression", ... + assert os.path.isabs(ret), ret + # Note: os.stat() may return False even if the file is there + # hence we skip the test, see: + # http://stackoverflow.com/questions/3112546/os-path-exists-lies + if POSIX and os.path.isfile(ret): + if hasattr(os, 'access') and hasattr(os, "X_OK"): + # XXX: may fail on MACOS + try: + assert os.access(ret, os.X_OK) + except AssertionError: + if os.path.exists(ret) and not CI_TESTING: + raise + + def pid(self, ret, info): + self.assertIsInstance(ret, int) + self.assertGreaterEqual(ret, 0) + + def ppid(self, ret, info): + self.assertIsInstance(ret, (int, long)) + self.assertGreaterEqual(ret, 0) + proc_info(ret) + + def name(self, ret, info): + self.assertIsInstance(ret, (str, unicode)) + if WINDOWS and not ret and is_win_secure_system_proc(info['pid']): + # https://github.com/giampaolo/psutil/issues/2338 + return + # on AIX, "" processes don't have names + if not AIX: + assert ret, repr(ret) + + def create_time(self, ret, info): + self.assertIsInstance(ret, float) + try: + self.assertGreaterEqual(ret, 0) + except AssertionError: + # XXX + if OPENBSD and info['status'] == psutil.STATUS_ZOMBIE: + pass + else: + raise + # this can't be taken for granted on all platforms + # self.assertGreaterEqual(ret, psutil.boot_time()) + # make sure returned value can be pretty printed + # with strftime + time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret)) + + def uids(self, ret, info): + assert is_namedtuple(ret) + for uid in ret: + self.assertIsInstance(uid, int) + self.assertGreaterEqual(uid, 0) + + def gids(self, ret, info): + assert is_namedtuple(ret) + # note: testing all gids as above seems not to be reliable for + # gid == 30 (nodoby); not sure why. + for gid in ret: + self.assertIsInstance(gid, int) + if not MACOS and not NETBSD: + self.assertGreaterEqual(gid, 0) + + def username(self, ret, info): + self.assertIsInstance(ret, str) + self.assertEqual(ret.strip(), ret) + assert ret.strip() + + def status(self, ret, info): + self.assertIsInstance(ret, str) + assert ret, ret + self.assertNotEqual(ret, '?') # XXX + self.assertIn(ret, VALID_PROC_STATUSES) + + def io_counters(self, ret, info): + assert is_namedtuple(ret) + for field in ret: + self.assertIsInstance(field, (int, long)) + if field != -1: + self.assertGreaterEqual(field, 0) + + def ionice(self, ret, info): + if LINUX: + self.assertIsInstance(ret.ioclass, int) + self.assertIsInstance(ret.value, int) + self.assertGreaterEqual(ret.ioclass, 0) + self.assertGreaterEqual(ret.value, 0) + else: # Windows, Cygwin + choices = [ + psutil.IOPRIO_VERYLOW, + psutil.IOPRIO_LOW, + psutil.IOPRIO_NORMAL, + psutil.IOPRIO_HIGH, + ] + self.assertIsInstance(ret, int) + self.assertGreaterEqual(ret, 0) + self.assertIn(ret, choices) + + def num_threads(self, ret, info): + self.assertIsInstance(ret, int) + if WINDOWS and ret == 0 and is_win_secure_system_proc(info['pid']): + # https://github.com/giampaolo/psutil/issues/2338 + return + self.assertGreaterEqual(ret, 1) + + def threads(self, ret, info): + self.assertIsInstance(ret, list) + for t in ret: + assert is_namedtuple(t) + self.assertGreaterEqual(t.id, 0) + self.assertGreaterEqual(t.user_time, 0) + self.assertGreaterEqual(t.system_time, 0) + for field in t: + self.assertIsInstance(field, (int, float)) + + def cpu_times(self, ret, info): + assert is_namedtuple(ret) + for n in ret: + self.assertIsInstance(n, float) + self.assertGreaterEqual(n, 0) + # TODO: check ntuple fields + + def cpu_percent(self, ret, info): + self.assertIsInstance(ret, float) + assert 0.0 <= ret <= 100.0, ret + + def cpu_num(self, ret, info): + self.assertIsInstance(ret, int) + if FREEBSD and ret == -1: + return + self.assertGreaterEqual(ret, 0) + if psutil.cpu_count() == 1: + self.assertEqual(ret, 0) + self.assertIn(ret, list(range(psutil.cpu_count()))) + + def memory_info(self, ret, info): + assert is_namedtuple(ret) + for value in ret: + self.assertIsInstance(value, (int, long)) + self.assertGreaterEqual(value, 0) + if WINDOWS: + self.assertGreaterEqual(ret.peak_wset, ret.wset) + self.assertGreaterEqual(ret.peak_paged_pool, ret.paged_pool) + self.assertGreaterEqual(ret.peak_nonpaged_pool, ret.nonpaged_pool) + self.assertGreaterEqual(ret.peak_pagefile, ret.pagefile) + + def memory_full_info(self, ret, info): + assert is_namedtuple(ret) + total = psutil.virtual_memory().total + for name in ret._fields: + value = getattr(ret, name) + self.assertIsInstance(value, (int, long)) + self.assertGreaterEqual(value, 0, msg=(name, value)) + if LINUX or OSX and name in ('vms', 'data'): + # On Linux there are processes (e.g. 'goa-daemon') whose + # VMS is incredibly high for some reason. + continue + self.assertLessEqual(value, total, msg=(name, value, total)) + + if LINUX: + self.assertGreaterEqual(ret.pss, ret.uss) + + def open_files(self, ret, info): + self.assertIsInstance(ret, list) + for f in ret: + self.assertIsInstance(f.fd, int) + self.assertIsInstance(f.path, str) + self.assertEqual(f.path.strip(), f.path) + if WINDOWS: + self.assertEqual(f.fd, -1) + elif LINUX: + self.assertIsInstance(f.position, int) + self.assertIsInstance(f.mode, str) + self.assertIsInstance(f.flags, int) + self.assertGreaterEqual(f.position, 0) + self.assertIn(f.mode, ('r', 'w', 'a', 'r+', 'a+')) + self.assertGreater(f.flags, 0) + elif BSD and not f.path: + # XXX see: https://github.com/giampaolo/psutil/issues/595 + continue + assert os.path.isabs(f.path), f + try: + st = os.stat(f.path) + except FileNotFoundError: + pass + else: + assert stat.S_ISREG(st.st_mode), f + + def num_fds(self, ret, info): + self.assertIsInstance(ret, int) + self.assertGreaterEqual(ret, 0) + + def connections(self, ret, info): + with create_sockets(): + self.assertEqual(len(ret), len(set(ret))) + for conn in ret: + assert is_namedtuple(conn) + check_connection_ntuple(conn) + + def cwd(self, ret, info): + self.assertIsInstance(ret, (str, unicode)) + self.assertEqual(ret.strip(), ret) + if ret: + assert os.path.isabs(ret), ret + try: + st = os.stat(ret) + except OSError as err: + if WINDOWS and psutil._psplatform.is_permission_err(err): + pass + # directory has been removed in mean time + elif err.errno != errno.ENOENT: + raise + else: + assert stat.S_ISDIR(st.st_mode) + + def memory_percent(self, ret, info): + self.assertIsInstance(ret, float) + assert 0 <= ret <= 100, ret + + def is_running(self, ret, info): + self.assertIsInstance(ret, bool) + + def cpu_affinity(self, ret, info): + self.assertIsInstance(ret, list) + self.assertNotEqual(ret, []) + cpus = list(range(psutil.cpu_count())) + for n in ret: + self.assertIsInstance(n, int) + self.assertIn(n, cpus) + + def terminal(self, ret, info): + self.assertIsInstance(ret, (str, type(None))) + if ret is not None: + assert os.path.isabs(ret), ret + assert os.path.exists(ret), ret + + def memory_maps(self, ret, info): + for nt in ret: + self.assertIsInstance(nt.addr, str) + self.assertIsInstance(nt.perms, str) + self.assertIsInstance(nt.path, str) + for fname in nt._fields: + value = getattr(nt, fname) + if fname == 'path': + if not value.startswith(("[", "anon_inode:")): + assert os.path.isabs(nt.path), nt.path + # commented as on Linux we might get + # '/foo/bar (deleted)' + # assert os.path.exists(nt.path), nt.path + elif fname == 'addr': + assert value, repr(value) + elif fname == 'perms': + if not WINDOWS: + assert value, repr(value) + else: + self.assertIsInstance(value, (int, long)) + self.assertGreaterEqual(value, 0) + + def num_handles(self, ret, info): + self.assertIsInstance(ret, int) + self.assertGreaterEqual(ret, 0) + + def nice(self, ret, info): + self.assertIsInstance(ret, int) + if POSIX: + assert -20 <= ret <= 20, ret + else: + priorities = [ + getattr(psutil, x) + for x in dir(psutil) + if x.endswith('_PRIORITY_CLASS') + ] + self.assertIn(ret, priorities) + if PY3: + self.assertIsInstance(ret, enum.IntEnum) + else: + self.assertIsInstance(ret, int) + + def num_ctx_switches(self, ret, info): + assert is_namedtuple(ret) + for value in ret: + self.assertIsInstance(value, (int, long)) + self.assertGreaterEqual(value, 0) + + def rlimit(self, ret, info): + self.assertIsInstance(ret, tuple) + self.assertEqual(len(ret), 2) + self.assertGreaterEqual(ret[0], -1) + self.assertGreaterEqual(ret[1], -1) + + def environ(self, ret, info): + self.assertIsInstance(ret, dict) + for k, v in ret.items(): + self.assertIsInstance(k, str) + self.assertIsInstance(v, str) + + +if __name__ == '__main__': + from psutil.tests.runner import run_from_name + + run_from_name(__file__) diff --git a/psutil/tests/test_testutils.py b/psutil/tests/test_testutils.py index e92c4e11e..a93f9f09f 100755 --- a/psutil/tests/test_testutils.py +++ b/psutil/tests/test_testutils.py @@ -36,6 +36,7 @@ from psutil.tests import call_until from psutil.tests import chdir from psutil.tests import create_sockets +from psutil.tests import filter_proc_connections from psutil.tests import get_free_port from psutil.tests import is_namedtuple from psutil.tests import mock @@ -318,14 +319,18 @@ def tcp_tcp_socketpair(self): def test_unix_socketpair(self): p = psutil.Process() num_fds = p.num_fds() - self.assertEqual(p.connections(kind='unix'), []) + self.assertEqual( + filter_proc_connections(p.connections(kind='unix')), [] + ) name = self.get_testfn() server, client = unix_socketpair(name) try: assert os.path.exists(name) assert stat.S_ISSOCK(os.stat(name).st_mode) self.assertEqual(p.num_fds() - num_fds, 2) - self.assertEqual(len(p.connections(kind='unix')), 2) + self.assertEqual( + len(filter_proc_connections(p.connections(kind='unix'))), 2 + ) self.assertEqual(server.getsockname(), name) self.assertEqual(client.getpeername(), name) finally: @@ -374,10 +379,10 @@ def test_leak_mem(self): ls = [] def fun(ls=ls): - ls.append("x" * 24 * 1024) + ls.append("x" * 124 * 1024) try: - # will consume around 3M in total + # will consume around 30M in total self.assertRaisesRegex( AssertionError, "extra-mem", self.execute, fun, times=50 ) diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 158c46eec..43db68fd9 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -397,6 +397,12 @@ def test_process(): sh("%s psutil\\tests\\test_process.py" % PYTHON) +def test_process_all(): + """Run process all tests.""" + build() + sh("%s psutil\\tests\\test_process_all.py" % PYTHON) + + def test_system(): """Run system tests.""" build() @@ -517,23 +523,15 @@ def get_python(path): # try to look for a python installation given a shortcut name path = path.replace('.', '') vers = ( - '26', - '26-32', - '26-64', '27', '27-32', '27-64', - '36', - '36-32', - '36-64', - '37', - '37-32', - '37-64', - '38', - '38-32', - '38-64', - '39-32', - '39-64', + '310-32', + '310-64', + '311-32', + '311-64', + '312-32', + '312-64', ) for v in vers: pypath = r'C:\\python%s\python.exe' % v @@ -571,6 +569,7 @@ def parse_args(): sp.add_parser('test-misc', help="run misc tests") sp.add_parser('test-platform', help="run windows only tests") sp.add_parser('test-process', help="run process tests") + sp.add_parser('test-process-all', help="run process all tests") sp.add_parser('test-system', help="run system tests") sp.add_parser('test-unicode', help="run unicode tests") sp.add_parser('test-testutils', help="run test utils tests") From 20ba2662bbd66e01d2ce0d93b84eb8db9e723542 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 7 Jan 2024 12:19:40 +0100 Subject: [PATCH 09/10] more tests refactoring --- psutil/tests/__init__.py | 69 +++++++++++++++++++++--------------- psutil/tests/test_process.py | 30 +++++++--------- psutil/tests/test_unicode.py | 19 ++++------ 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index df30e3069..336e17644 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -100,7 +100,7 @@ 'process_namespace', 'system_namespace', 'print_sysinfo', 'is_win_secure_system_proc', # fs utils - 'chdir', 'safe_rmpath', 'create_exe', 'get_testfn', + 'chdir', 'safe_rmpath', 'create_py_exe', 'create_c_exe', 'get_testfn', # os 'get_winver', 'kernel_version', # sync primitives @@ -377,7 +377,7 @@ def spawn_testproc(cmd=None, **kwds): pyline = ( "from time import sleep;" + "open(r'%s', 'w').close();" % testfn - + "sleep(60);" + + "[sleep(0.1) for x in range(100)];" # 10 secs ) cmd = [PYTHON_EXE, "-c", pyline] sproc = subprocess.Popen(cmd, **kwds) @@ -853,33 +853,41 @@ def chdir(dirname): os.chdir(curdir) -def create_exe(outpath, c_code=None): - """Creates an executable file in the given location.""" - assert not os.path.exists(outpath), outpath - if c_code: - if not which("gcc"): - raise unittest.SkipTest("gcc is not installed") - if isinstance(c_code, bool): # c_code is True - c_code = textwrap.dedent(""" - #include - int main() { - pause(); - return 1; - } - """) - assert isinstance(c_code, str), c_code - with open(get_testfn(suffix='.c'), "w") as f: - f.write(c_code) - try: - subprocess.check_call(["gcc", f.name, "-o", outpath]) - finally: - safe_rmpath(f.name) +def create_py_exe(path): + """Create a Python executable file in the given location.""" + assert not os.path.exists(path), path + atexit.register(safe_rmpath, path) + shutil.copyfile(PYTHON_EXE, path) + if POSIX: + st = os.stat(path) + os.chmod(path, st.st_mode | stat.S_IEXEC) + return path + + +def create_c_exe(path, c_code=None): + """Create a compiled C executable in the given location.""" + assert not os.path.exists(path), path + if not which("gcc"): + raise unittest.SkipTest("gcc is not installed") + if c_code is None: + c_code = textwrap.dedent(""" + #include + int main() { + pause(); + return 1; + } + """) else: - # copy python executable - shutil.copyfile(PYTHON_EXE, outpath) - if POSIX: - st = os.stat(outpath) - os.chmod(outpath, st.st_mode | stat.S_IEXEC) + assert isinstance(c_code, str), c_code + + atexit.register(safe_rmpath, path) + with open(get_testfn(suffix='.c'), "w") as f: + f.write(c_code) + try: + subprocess.check_call(["gcc", f.name, "-o", path]) + finally: + safe_rmpath(f.name) + return path def get_testfn(suffix="", dir=None): @@ -891,7 +899,9 @@ def get_testfn(suffix="", dir=None): while True: name = tempfile.mktemp(prefix=TESTFN_PREFIX, suffix=suffix, dir=dir) if not os.path.exists(name): # also include dirs - return os.path.realpath(name) # needed for OSX + path = os.path.realpath(name) # needed for OSX + atexit.register(safe_rmpath, path) + return path # =================================================================== @@ -940,6 +950,7 @@ class PsutilTestCase(TestCase): """ def get_testfn(self, suffix="", dir=None): + suffix += "-" + self.id() # add the test name fname = get_testfn(suffix=suffix, dir=dir) self.addCleanup(safe_rmpath, fname) return fname diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index f03a22da0..fdd90dcbe 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -57,7 +57,8 @@ from psutil.tests import ThreadTask from psutil.tests import call_until from psutil.tests import copyload_shared_lib -from psutil.tests import create_exe +from psutil.tests import create_c_exe +from psutil.tests import create_py_exe from psutil.tests import mock from psutil.tests import process_namespace from psutil.tests import reap_children @@ -768,9 +769,8 @@ def test_name(self): @unittest.skipIf(PYPY, "unreliable on PYPY") def test_long_name(self): - testfn = self.get_testfn(suffix="0123456789" * 2) - create_exe(testfn) - cmdline = [testfn, "-c", "time.sleep(10)"] + pyexe = create_py_exe(self.get_testfn(suffix="0123456789" * 2)) + cmdline = [pyexe, "-c", "time.sleep(10)"] p = self.spawn_psproc(cmdline) if OPENBSD: # XXX: for some reason the test process may turn into a @@ -781,14 +781,14 @@ def test_long_name(self): # just compare the first 15 chars. Full explanation: # https://github.com/giampaolo/psutil/issues/2239 try: - self.assertEqual(p.name(), os.path.basename(testfn)) + self.assertEqual(p.name(), os.path.basename(pyexe)) except AssertionError: if p.status() == psutil.STATUS_ZOMBIE: - assert os.path.basename(testfn).startswith(p.name()) + assert os.path.basename(pyexe).startswith(p.name()) else: raise else: - self.assertEqual(p.name(), os.path.basename(testfn)) + self.assertEqual(p.name(), os.path.basename(pyexe)) # XXX @unittest.skipIf(SUNOS, "broken on SUNOS") @@ -798,15 +798,12 @@ def test_prog_w_funky_name(self): # Test that name(), exe() and cmdline() correctly handle programs # with funky chars such as spaces and ")", see: # https://github.com/giampaolo/psutil/issues/628 - funky_path = self.get_testfn(suffix='foo bar )') - create_exe(funky_path) - cmdline = [funky_path, "-c", "time.sleep(10)"] + pyexe = create_py_exe(self.get_testfn(suffix='foo bar )')) + cmdline = [pyexe, "-c", "time.sleep(10)"] p = self.spawn_psproc(cmdline) self.assertEqual(p.cmdline(), cmdline) - self.assertEqual(p.name(), os.path.basename(funky_path)) - self.assertEqual( - os.path.normcase(p.exe()), os.path.normcase(funky_path) - ) + self.assertEqual(p.name(), os.path.basename(pyexe)) + self.assertEqual(os.path.normcase(p.exe()), os.path.normcase(pyexe)) @unittest.skipIf(not POSIX, 'POSIX only') def test_uids(self): @@ -1477,10 +1474,9 @@ def test_weird_environ(self): return execve("/bin/cat", argv, envp); } """) - path = self.get_testfn() - create_exe(path, c_code=code) + cexe = create_c_exe(self.get_testfn(), c_code=code) sproc = self.spawn_testproc( - [path], stdin=subprocess.PIPE, stderr=subprocess.PIPE + [cexe], stdin=subprocess.PIPE, stderr=subprocess.PIPE ) p = psutil.Process(sproc.pid) wait_for_pid(p.pid) diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index 3ab71b03a..aeac62ce5 100755 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -100,7 +100,7 @@ from psutil.tests import bind_unix_socket from psutil.tests import chdir from psutil.tests import copyload_shared_lib -from psutil.tests import create_exe +from psutil.tests import create_py_exe from psutil.tests import get_testfn from psutil.tests import safe_mkdir from psutil.tests import safe_rmpath @@ -139,7 +139,7 @@ def try_unicode(suffix): testfn = get_testfn(suffix=suffix) try: safe_rmpath(testfn) - create_exe(testfn) + create_py_exe(testfn) sproc = spawn_testproc(cmd=[testfn]) shutil.copyfile(testfn, testfn + '-2') safe_rmpath(testfn + '-2') @@ -165,9 +165,13 @@ class BaseUnicodeTest(PsutilTestCase): def setUpClass(cls): super().setUpClass() cls.skip_tests = False + cls.funky_name = None if cls.funky_suffix is not None: if not try_unicode(cls.funky_suffix): cls.skip_tests = True + else: + cls.funky_name = get_testfn(suffix=cls.funky_suffix) + create_py_exe(cls.funky_name) def setUp(self): super().setUp() @@ -183,17 +187,6 @@ class TestFSAPIs(BaseUnicodeTest): funky_suffix = UNICODE_SUFFIX - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.funky_name = get_testfn(suffix=cls.funky_suffix) - create_exe(cls.funky_name) - - @classmethod - def tearDownClass(cls): - super().tearDownClass() - safe_rmpath(cls.funky_name) - def expect_exact_path_match(self): # Do not expect psutil to correctly handle unicode paths on # Python 2 if os.listdir() is not able either. From d4ae6a0b527407bcbea09654528129e58e9787bd Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 12 Jan 2024 23:20:45 +0100 Subject: [PATCH 10/10] refact some tests --- psutil/tests/__init__.py | 3 ++- psutil/tests/test_process.py | 2 +- psutil/tests/test_unicode.py | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index 336e17644..4d8e35583 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -757,6 +757,8 @@ def wait_for_pid(pid): """Wait for pid to show up in the process list then return. Used in the test suite to give time the sub process to initialize. """ + if pid not in psutil.pids(): + raise psutil.NoSuchProcess(pid) psutil.Process(pid) if WINDOWS: # give it some more time to allow better initialization @@ -950,7 +952,6 @@ class PsutilTestCase(TestCase): """ def get_testfn(self, suffix="", dir=None): - suffix += "-" + self.id() # add the test name fname = get_testfn(suffix=suffix, dir=dir) self.addCleanup(safe_rmpath, fname) return fname diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py index fdd90dcbe..b37944a87 100755 --- a/psutil/tests/test_process.py +++ b/psutil/tests/test_process.py @@ -762,7 +762,7 @@ def test_long_cmdline(self): self.assertEqual(p.cmdline(), cmdline) def test_name(self): - p = self.spawn_psproc(PYTHON_EXE) + p = self.spawn_psproc() name = p.name().lower() pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py index aeac62ce5..ab06cebed 100755 --- a/psutil/tests/test_unicode.py +++ b/psutil/tests/test_unicode.py @@ -224,9 +224,7 @@ def test_proc_cmdline(self): for part in cmdline: self.assertIsInstance(part, str) if self.expect_exact_path_match(): - self.assertEqual( - cmdline, [self.funky_name, "-c", "time.sleep(10)"] - ) + self.assertEqual(cmdline, cmd) def test_proc_cwd(self): dname = self.funky_name + "2"