From 79b059a76f42dc0c5866f32dca1b58a2750ea1a3 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 1 May 2016 05:00:55 +0200 Subject: [PATCH] refactor num_threads --- HISTORY.rst | 1 + psutil/_pslinux.py | 16 ++++++++++------ psutil/tests/test_linux.py | 13 ++++++------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 5f8003132..d98023e7a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,6 +12,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - Process.ppid() is 20% faster - Process.status() is 28% faster - Process.name() is 25% faster + - Process.num_threads is 20% faster on Python 3 **Bug fixes** diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 9cb34acad..7da946ea3 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -967,6 +967,10 @@ def _parse_stat_file(self): fields_after_name = data[rpar + 2:].split() return [name] + fields_after_name + def _read_status_file(self): + with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f: + return f.read() + @wrap_exceptions def name(self): name = self._parse_stat_file()[0] @@ -1208,12 +1212,12 @@ def num_ctx_switches(self): "probably older than 2.6.23" % self.pid) @wrap_exceptions - def num_threads(self): - with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f: - for line in f: - if line.startswith(b"Threads:"): - return int(line.split()[1]) - raise NotImplementedError("line not found") + def num_threads(self, _num_threads_re=re.compile(b'Threads:\t(\d+)')): + # Note: on Python 3 using a re is faster than iterating over file + # line by line. On Python 2 is the exact opposite, and iterating + # over a file on Python 3 is slower than on Python 2. + data = self._read_status_file() + return int(_num_threads_re.findall(data)[0]) @wrap_exceptions def threads(self): diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index cbe56b84c..a5cd6559b 100644 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -804,6 +804,12 @@ def test_compare_stat_and_status_files(self): elif line.startswith('PPid:'): ppid = int(line.split()[1]) self.assertEqual(p.ppid(), ppid) + elif line.startswith('Threads:'): + # num_threads() is determined via 'status' file + # but we use a re internally, so let's check it + # also. + num_threads = int(line.split()[1]) + self.assertEqual(p.num_threads(), num_threads) def test_memory_maps(self): src = textwrap.dedent(""" @@ -924,13 +930,6 @@ def test_num_ctx_switches_mocked(self): psutil._pslinux.Process(os.getpid()).num_ctx_switches) assert m.called - def test_num_threads_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).num_threads) - assert m.called - def test_uids_mocked(self): with mock.patch('psutil._pslinux.open', create=True) as m: self.assertRaises(