Package: helpdev
Version: 0.7.1-7
Severity: normal
Tags: patch
Dear Maintainer,
(I am the maintainer; filing this so the problem is on record before the fix,
since it affects the version currently in unstable.)
helpdev aborts with an unhandled traceback when pip is not installed:
$ helpdev --packages-pip
Traceback (most recent call last):
...
File "/usr/lib/python3/dist-packages/helpdev/__init__.py", line 73, in
_run_subprocess_split
output = subprocess.check_output(command, shell=False)
FileNotFoundError: [Errno 2] No such file or directory: 'pip'
Debian does not install pip by default and helpdev does not depend on it, so
this is the behaviour on a stock system.
Affected invocations, tested on a machine with no pip on PATH:
helpdev completes normally (does not reach the pip path)
helpdev --all FileNotFoundError
helpdev --packages-pip FileNotFoundError
helpdev --packages FileNotFoundError
--all matters most: it is the obvious invocation for a tool whose whole
purpose is reporting environment information, and it is the one documented
first in helpdev(1).
CAUSE
check_python_packages() in helpdev/__init__.py calls the subprocess helper
directly:
all_packages = _run_subprocess_split(['pip', 'list'])
and _run_subprocess_split() is a bare subprocess.check_output(), so a missing
pip propagates out of main().
The interesting part is that upstream already solved this for the other
external tool it calls. check_conda_packages(), twenty lines further down,
does:
try:
all_packages = _run_subprocess_split(['conda', 'list', ...])
except (subprocess.CalledProcessError, FileNotFoundError2and3):
info['CONDA PACKAGES']['Status'] = 'Conda not available!'
else:
...
That works correctly - with no conda installed, helpdev reports
"Conda not available!" and carries on. Upstream even defines a
FileNotFoundError2and3 compatibility alias specifically to catch this. The
guard was simply never applied to the pip path.
FIX
The patch attached to this report (0001-report-missing-pip-instead-of-
crashing.patch) applies the existing conda pattern to check_python_packages(),
so a missing pip is reported as "PIP not available!" alongside the existing
"Conda not available!" line, and no invocation crashes.
I have deliberately not added a dependency on python3-pip. pip is
heavyweight, Debian discourages relying on it at runtime, and a tool whose
job is to report what is present in an environment should say when something
is absent rather than refuse to run without it. Suggests: python3-pip would
be reasonable to add alongside this.
The patch is against 0.7.1-7. With it applied:
* helpdev --all, --packages-pip and --packages-pip-e all complete, printing
"PIP not available!" under PYTHON PACKAGES.
* The package builds in a clean unstable chroot and the test suite passes
(5 passed). python3-pip is a build dependency, so the tests still
exercise the real pip code path, which is unchanged.
* lintian reports nothing new.
I will forward it upstream to https://gitlab.com/dpizetta/helpdev as well.
Reproduced and fixed against 0.7.1-7 as built from the packaging git at
31ca302. Testing was done in clean unstable chroots rather than on an
installed system: sbuild for the build and test suite, autopkgtest under
unshare for the runtime check, and the crash itself reproduced by importing
the module directly on a host with no pip on PATH. I have not tried to
reproduce it on a stock installed helpdev, since the code path does not
depend on how the package was installed.
Description: report a missing pip instead of crashing
check_python_packages() shells out to "pip list" via _run_subprocess_split(),
but nothing catches the FileNotFoundError raised when pip is absent. Debian
does not install pip by default, so on a stock system "helpdev --all" and
"helpdev --packages-pip" abort with an unhandled traceback rather than
reporting what they could not determine.
.
The sibling function check_conda_packages() already handles exactly this
case: it wraps the same subprocess helper in a try/except for
CalledProcessError and the upstream FileNotFoundError2and3 compatibility
alias, and records a Status string instead of propagating. This applies that
existing pattern to the pip path, so a missing pip reports
"PIP not available!" just as a missing conda reports "Conda not available!".
.
Adding a dependency on python3-pip would be the wrong fix: pip is heavyweight
and Debian discourages relying on it at runtime. A diagnostics tool should
report an absent tool, not require it.
Author: Dustin Kost <[email protected]>
Forwarded: no, to be submitted to https://gitlab.com/dpizetta/helpdev
Last-Update: 2026-08-14
---
--- a/helpdev/__init__.py
+++ b/helpdev/__init__.py
@@ -497,27 +497,30 @@ def check_python_packages(edit_mode=Fals
dict(str): Dictionary filled with respective information.
"""
+ info = {'PYTHON PACKAGES': {}}
all_packages = ''
- if edit_mode:
- all_packages = _run_subprocess_split(['pip', 'list', '-e'])
+ try:
+ if edit_mode:
+ all_packages = _run_subprocess_split(['pip', 'list', '-e'])
+ else:
+ # list all packages, including in editable mode
+ all_packages = _run_subprocess_split(['pip', 'list'])
+ except (subprocess.CalledProcessError, FileNotFoundError2and3):
+ info['PYTHON PACKAGES']['Status'] = 'PIP not available!'
else:
- # list all packages, including in editable mode
- all_packages = _run_subprocess_split(['pip', 'list'])
-
- # split lines and remove table name
- line_packages = all_packages.split("\n")[2:]
-
- info = {'PYTHON PACKAGES': {}}
-
- # clean spaces, create a list and insert in the dictionary
- for line in line_packages:
- splitted = line.split(' ')
- cleaned = ' '.join(splitted).split()
- info['PYTHON PACKAGES'][cleaned[0]] = cleaned[1]
+ # split lines and remove table name
+ line_packages = all_packages.split("\n")[2:]
- if packages:
- info['PYTHON PACKAGES'] = filter_packages(info['PYTHON PACKAGES'], packages)
+ # clean spaces, create a list and insert in the dictionary
+ for line in line_packages:
+ splitted = line.split(' ')
+ cleaned = ' '.join(splitted).split()
+ info['PYTHON PACKAGES'][cleaned[0]] = cleaned[1]
+
+ if packages:
+ info['PYTHON PACKAGES'] = filter_packages(info['PYTHON PACKAGES'],
+ packages)
return info