summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2023-04-17 01:01:44 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2023-04-17 01:01:44 +0200
commit0e3f6c465181734181f2428b4a99429a82f3fc45 (patch)
tree6c2ea5784e72d8528e84cfdc53a471a7942b6b8c
parentb070015104ea01689fee9f7c91709c0e2d35a9a8 (diff)
downloadpsutil-0e3f6c465181734181f2428b4a99429a82f3fc45.tar.gz
fix #2238 if cwd() cannot be determined always return "" instead of None
-rw-r--r--HISTORY.rst8
-rw-r--r--docs/index.rst11
-rw-r--r--psutil/_psaix.py2
-rw-r--r--psutil/_psbsd.py2
-rw-r--r--psutil/_pssunos.py2
-rw-r--r--psutil/arch/netbsd/proc.c9
-rw-r--r--psutil/arch/openbsd/proc.c4
-rwxr-xr-xpsutil/tests/test_contracts.py16
-rwxr-xr-xpsutil/tests/test_posix.py7
9 files changed, 34 insertions, 27 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index d9463817..97c2c20e 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -15,6 +15,10 @@
empty string)
- The function is faster since it no longer iterates over all processes.
- No longer produces duplicate connection entries.
+- 2238_: there are cases where `Process.cwd()`_ cannot be determined
+ (e.g. directory no longer exists), in which case we returned either ``None``
+ or an empty string. This was consolidated and we now return ``""`` on all
+ platforms.
**Bug fixes**
@@ -39,8 +43,8 @@
*used* are too high. We now match values shown by *htop* CLI utility.
- 2236_, [NetBSD]: `Process.num_threads()`_ and `Process.threads()`_ return
threads that are already terminated.
-- 2237_, [OpenBSD]: `Process.cwd()`_ may raise ``FileNotFoundError`` if cwd no
- longer exists. Return ``None`` instead.
+- 2237_, [OpenBSD], [NetBSD]: `Process.cwd()`_ may raise ``FileNotFoundError``
+ if cwd no longer exists. Return an empty string instead.
5.9.4
=====
diff --git a/docs/index.rst b/docs/index.rst
index 2a1c5b6a..6f0132d2 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1180,9 +1180,10 @@ Process class
.. method:: exe()
- The process executable as an absolute path.
- On some systems this may also be an empty string.
- The return value is cached after first call.
+ The process executable as an absolute path. On some systems, if exe cannot
+ be determined for some internal reason (e.g. system process or path no
+ longer exists), this may be an empty string. The return value is cached
+ after first call.
>>> import psutil
>>> psutil.Process().exe()
@@ -1281,7 +1282,9 @@ Process class
.. method:: cwd()
- The process current working directory as an absolute path.
+ The process current working directory as an absolute path. If cwd cannot be
+ determined for some internal reason (e.g. system process or directiory no
+ longer exists) it may return an empty string.
.. versionchanged:: 5.6.4 added support for NetBSD
diff --git a/psutil/_psaix.py b/psutil/_psaix.py
index 5c41069c..67f0314f 100644
--- a/psutil/_psaix.py
+++ b/psutil/_psaix.py
@@ -487,7 +487,7 @@ class Process(object):
return result.rstrip('/')
except FileNotFoundError:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
- return None
+ return ""
@wrap_exceptions
def memory_info(self):
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index 8fc256b2..b77a9d68 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -839,7 +839,7 @@ class Process(object):
elif NETBSD or HAS_PROC_OPEN_FILES:
# FreeBSD < 8 does not support functions based on
# kinfo_getfile() and kinfo_getvmmap()
- return cext.proc_cwd(self.pid) or None
+ return cext.proc_cwd(self.pid) or ""
else:
raise NotImplementedError(
"supported only starting from FreeBSD 8" if
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 8663de3c..d44bf2d7 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -542,7 +542,7 @@ class Process(object):
return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid))
except FileNotFoundError:
os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
- return None
+ return ""
@wrap_exceptions
def memory_info(self):
diff --git a/psutil/arch/netbsd/proc.c b/psutil/arch/netbsd/proc.c
index e71afb38..2688dced 100644
--- a/psutil/arch/netbsd/proc.c
+++ b/psutil/arch/netbsd/proc.c
@@ -115,10 +115,13 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
ssize_t len = readlink(buf, path, sizeof(path) - 1);
free(buf);
if (len == -1) {
- if (errno == ENOENT)
- NoSuchProcess("readlink -> ENOENT");
- else
+ if (errno == ENOENT) {
+ psutil_debug("sysctl(KERN_PROC_CWD) -> ENOENT converted to ''");
+ return Py_BuildValue("", "");
+ }
+ else {
PyErr_SetFromErrno(PyExc_OSError);
+ }
return NULL;
}
path[len] = '\0';
diff --git a/psutil/arch/openbsd/proc.c b/psutil/arch/openbsd/proc.c
index e66cac66..38538a4a 100644
--- a/psutil/arch/openbsd/proc.c
+++ b/psutil/arch/openbsd/proc.c
@@ -306,8 +306,8 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
int name[] = { CTL_KERN, KERN_PROC_CWD, pid };
if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) {
if (errno == ENOENT) {
- psutil_debug("sysctl(KERN_PROC_CWD) -> ENOENT converted to None");
- Py_RETURN_NONE; // mimic os.cpu_count()
+ psutil_debug("sysctl(KERN_PROC_CWD) -> ENOENT converted to ''");
+ return Py_BuildValue("", "");
}
else {
PyErr_SetFromErrno(PyExc_OSError);
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index 6859f69f..7f299a62 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -452,10 +452,9 @@ class TestFetchAllProcesses(PsutilTestCase):
self.assertIsInstance(part, str)
def exe(self, ret, info):
- self.assertIsInstance(ret, (str, unicode, type(None)))
- if not ret:
- self.assertEqual(ret, '')
- else:
+ 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
@@ -520,7 +519,8 @@ class TestFetchAllProcesses(PsutilTestCase):
def username(self, ret, info):
self.assertIsInstance(ret, str)
- assert ret
+ self.assertEqual(ret.strip(), ret)
+ assert ret.strip()
def status(self, ret, info):
self.assertIsInstance(ret, str)
@@ -619,6 +619,7 @@ class TestFetchAllProcesses(PsutilTestCase):
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:
@@ -651,8 +652,9 @@ class TestFetchAllProcesses(PsutilTestCase):
check_connection_ntuple(conn)
def cwd(self, ret, info):
- if ret: # 'ret' can be None or empty
- self.assertIsInstance(ret, str)
+ self.assertIsInstance(ret, (str, unicode))
+ self.assertEqual(ret.strip(), ret)
+ if ret:
assert os.path.isabs(ret), ret
try:
st = os.stat(ret)
diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py
index d2631215..8ea6cf7c 100755
--- a/psutil/tests/test_posix.py
+++ b/psutil/tests/test_posix.py
@@ -351,12 +351,7 @@ class TestSystemAPIs(PsutilTestCase):
self.assertEqual(u.name, users[idx])
self.assertEqual(u.terminal, terminals[idx])
if u.pid is not None: # None on OpenBSD
- p = psutil.Process(u.pid)
- # on macOS time is off by ~47 secs for some reason, but
- # the next test against 'who' CLI succeeds
- delta = 60 if MACOS else 1
- self.assertAlmostEqual(
- u.started, p.create_time(), delta=delta)
+ psutil.Process(u.pid)
@retry_on_failure()
def test_users_started(self):