commandnotfound
This package provides a wrapper of command-not-found commands:
- Apt-based distribution's
/usr/lib/command-not-found
- Yum-based distribution's
/usr/libexec/pk-command-not-found
You know that when you type a command and then try to run it, if the command is not found, you will be informed that there is a command with a similar name, or you will be prompted to install the package required by it.
I want my Python scripts that call such commands internally to also display this kind of friendly messages. However, the trick does not work when you try to run a command in subprocess.run
. It simply displays a "command not found" message.
The commandnotfound
wraps subprocess.run
, and when the command passed as its argument is not found, it invoke the command-not-found commands.
Note: Tested only on Ubuntu.
How it works
In some apt-based distributions such as Ubuntu, shows a hint as an error message, when a user types wrong command name, e.g.:
$ converT
Command 'converT' not found, did you mean:
command 'convert' from deb imagemagick-6.q16 (8:6.9.10.23+dfsg-2.1ubuntu11.4)
....
This help system is implemented as /usr/lib/command-not-found
, and the bash call it when a command is not found on PATH directories.
The help system does not work when you run command in a Python code with subprocess.run
, e.g.:
$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> subprocess.run(["converT"])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
....
FileNotFoundError: [Errno 2] No such file or directory: 'converT'
>>>
In short, the error message says that the command could not be found.
So the commandnotfound wrapper wraps subprocess.run
and detects FileNotFoundError
is thrown or not, and in case it thrown, call the help command /usr/lib/command-not-found
.
$ python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> import commandnotfound
>>> wrapped_run = commandnotfound.wrap(subprocess.run)
>>> wrapped_run(["converT"])
Command 'converT' not found, did you mean:
command 'convert' from deb imagemagick-6.q16 (8:6.9.10.23+dfsg-2.1ubuntu11.4)
....
FileNotFoundError: [Errno 2] No such file or directory: 'converT'
>>>
This time, the error message pointed out that the command name may be wrong!
Installation
Install:
python3 -m pip install commandnotfound
Uninstall:
python3 -m pip uninstall commandnotfound
API
wrap
:
Function Wraps subprocess.run
to catch a FileNotFoundException, run a command-not-found command, and re-raise the exception.
import subprocess
import commandnotfound
wrapped_run = commandnotfound.wrap(subprocess.run)
wrapped_run(["converT"])
You can wrap check_output
or check_run
of subprocess
module, if you need.
wrapped_check_output = commandnotfound.wrap(subprocess.check_output)
report
:
Function Runs a command-not-found command.
import subprocess
import commandnotfound
try:
subprocess.run(["converT"])
except FileNotFoundError as e:
commandnotfound.report(e)