2

My python script is being called by kea-dhcp, which has capability of executing external scripts (https://kea.readthedocs.io/en/latest/arm/hooks.html#run-script-run-script-support-for-external-hook-scripts for details)

import subprocess
...
subprocess.check_output(('bridge', arg1, arg2 ...), stderr=subprocess.STDOUT)

Where does python take information about location of bridge binary? I'm getting an error:

FileNotFoundError: [Errno 2] No such file or directory: 'bridge': 'bridge'

I don't think it has anything to do with PYTHONPATH since it is not a problem of importing modules. What can be possibly wrong?

UPDATE Following Charles Duffy's suggestion, I did:

res = subprocess.check_output([shutil.which('bridge'), '-j', 'fdb', 'show'], stderr=subprocess.STDOUT)

but got error (it points to above subprocess line):

   File "/lib64/python3.6/subprocess.py", line 356, in check_output
     **kwargs).stdout
   File "/lib64/python3.6/subprocess.py", line 423, in run
     with Popen(*popenargs, **kwargs) as process:
   File "/lib64/python3.6/subprocess.py", line 729, in __init__
     restore_signals, start_new_session)
   File "/lib64/python3.6/subprocess.py", line 1278, in _execute_child
     executable = os.fsencode(executable)
   File "/lib64/python3.6/os.py", line 800, in fsencode
     filename = fspath(filename)  # Does type-checking of `filename`.
 TypeError: expected str, bytes or os.PathLike object, not NoneType

I can't see what is wrong with that, looks perfectly fine.

8
  • 2
    Honestly, the first place I'd go would be logging PATH as seen by your Python process, and inspecting it with the location of the bridge executable you mean to run in mind. I added an answer suggesting the documentation-approved best-practice approach, but assuming you're on a common UNIXlike, the default behavior should generally be appropriate. Commented Sep 28, 2022 at 16:15
  • 1
    Is bridge actually on your path? Are you able to execute bridge at a command line? Commented Sep 29, 2022 at 2:51
  • Yes, it is on my path, bridge is available. Commented Sep 29, 2022 at 3:56
  • Not your interactive PATH, but the Python interpreter's PATH. Validate that they're the same. Commented Sep 29, 2022 at 11:29
  • (that's what I was instructing you to do with my very first comment, when asking you to log PATH as seen by your Python process) Commented Sep 29, 2022 at 11:57

2 Answers 2

2

How unqualified names are handled by subprocess depends on which operating system you're on. Sometimes PATH is honored; never is PYTHONPATH used for the purpose.

To explicitly perform a PATH lookup, change your code to use shutil.which():

import shutil, subprocess

subprocess.check_output([shutil.which('bridge'), arg1, arg2], stderr=subprocess.STDOUT)

Quoting from https://docs.python.org/3/library/subprocess.html#popen-constructor (formatting from the original) --

Warning For maximum reliability, use a fully qualified path for the executable. To search for an unqualified name on PATH, use shutil.which(). On all platforms, passing sys.executable is the recommended way to launch the current Python interpreter again, and use the -m command-line format to launch an installed module.

Resolving the path of executable (or the first item of args) is platform dependent. For POSIX, see os.execvpe(), and note that when resolving or searching for the executable path, cwd overrides the current working directory and env can override the PATH environment variable. For Windows, see the documentation of the lpApplicationName and lpCommandLine parameters of WinAPI CreateProcess, and note that when resolving or searching for the executable path with shell=False, cwd does not override the current working directory and env cannot override the PATH environment variable. Using a full path avoids all of these variations.

Sign up to request clarification or add additional context in comments.

6 Comments

shutil.which() returning None tells you that it agrees that bridge isn't in the PATH.
shutil.which() returning None tells you that it agrees that bridge isn't in the PATH. Then this means that PATH passed to python interpreter is not complete, or it is not available. For whatever reason.
Right. Have you adjusted your Python program to log os.environ['PATH']?
Right. Have you adjusted your Python program to log os.environ['PATH']? Yes, I did print(os.environ['PATH']) and get KeyError: 'PATH', so python does not know PATH.
There's your problem, then. How is the Python process started?
|
0

The problem is that kea-dhcp does not export system environment variables to scripts it executes, it only supplies its specific environment. Discovered this via os.environ. The only solution to this is provide the full pass to bridge command (or any other launched in this context).

2 Comments

Easier to tell kea-dhcp, instead of directly running your Python script, to run /usr/bin/env PATH=/bin:/usr/bin:/usr/local/bin /path/to/your-python-script -- assuming that bridge is in one of those three locations; amend appropriately otherwise.
That said, under the circumstances, I'd suggest deleting the question and asking a new one, and posting this answer there. This is still posed as a question about Python, but Python has nothing to do with the problem at all; if you'd answered all the comments on the question asking you to confirm that PATH was correct in an accurate manner, we would have gotten to a solution much, much earlier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.