I have a simple Firefox extension, I have pretty much figured out the Firefox side of things (JavaScript, DOM and starting the Python program).
To explain how everything is supposed to work:
- A event occurs in the browser
- Firefox launches the Python program on the local system (achieved with Native Messaging)
- Firefox passes a one time message to the Python program via stdin
After step 3, Firefox is meant to exit the picture and Python is supposed to take over.
I am stuck on the Python part of this process. The python program does receive the message from Firefox, via stdin. But once execution goes past the line receivedMessage = getMessage()
, I start to get odd behaviour. For example the last line subprocess.Popen(...
is never executed. Even if I were to launch the Python program manually, say double clicking it in File explorer, the last line never executes.
The only way to make it execute is by commenting out receivedMessage = getMessage()
.
import subprocess
import json
import struct
import sys
def getMessage():
rawLength = sys.stdin.buffer.read(4)
messageLength = struct.unpack('@I', rawLength)[0]
message = sys.stdin.buffer.read(messageLength).decode('utf-8')
return json.loads(message)
receivedMessage = getMessage()
#subprocess.Popen(["explorer", "C:/Temp"]) #Is never executed
subprocess.Popen(['pwsh', 'C:/Temp/testProg.ps1']) #Is never executed
The core of the program is an example I got from the MDN documentation page, that I reworked by getting rid of the redundant parts. I don’t know the technical details behind stdin and how its specifically implemented in Python, I understand it at a high level only.
What could be holding back the execution of the program? Could it be held up by Firefox still streaming data to it?
Any help would be greatly appreciated!
1 Like
It appears that the issue might be related to the fact that the Python script is waiting for more input from the standard input (sys.stdin
) before continuing. When you comment out the line receivedMessage = getMessage()
, the script does not block on stdin, and the subsequent lines are executed.
To resolve this, you can modify the getMessage()
function to handle cases where the input is not available immediately. Here’s an example using select
to check if there is input available before attempting to read:
import subprocess
import json
import struct
import sys
import select
def getMessage():
# Check if there is data available on stdin
while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
rawLength = sys.stdin.buffer.read(4)
messageLength = struct.unpack('@I', rawLength)[0]
message = sys.stdin.buffer.read(messageLength).decode('utf-8')
return json.loads(message)
return None # No message available yet
receivedMessage = getMessage()
if receivedMessage:
# subprocess.Popen(["explorer", "C:/Temp"]) # Uncomment if you want to execute this
subprocess.Popen(['pwsh', 'C:/Temp/testProg.ps1'])
else:
print("No message available yet.")
This modification ensures that the getMessage()
function does not block indefinitely and returns None
if there is no message available yet. The subsequent code checks if a message is received before attempting to execute the subprocess.
Make sure to test this modification with your specific use case and adjust as needed.
2 Likes
I completely forgot I asked this question here a few nights ago. I really am losing it staring at this colour coded pharases.
.
But I am really glad to have found your help, thank you indeed. I tried your code but nothing seems to happen on the Python/Outside Browser side of things.
In the case of the if receivedMessage:
condition, the ps1
file is not run.
Just to be sure, since no console windows open to see any print statements, I tried running the line subprocess.Popen(["explorer", "C:/Temp"])
in the else
block, like this:
import subprocess
import json
import struct
import sys
import select
def getMessage():
# Check if there is data available on stdin
while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
rawLength = sys.stdin.buffer.read(4)
messageLength = struct.unpack('@I', rawLength)[0]
message = sys.stdin.buffer.read(messageLength).decode('utf-8')
return json.loads(message)
return None # No message available yet
receivedMessage = getMessage()
if receivedMessage:
# subprocess.Popen(["explorer", "C:/Temp"]) # Uncomment if you want to execute this
subprocess.Popen(['pwsh', 'C:/Temp/testProg.ps1'])
else:
subprocess.Popen(["explorer", "C:/Temp"])
but in either the if
or else
case nothing seems to happen. If try to execute the python program itself directly in file explorer by double clicking it, nothign happens either, I thought maybe the else
condition would run.
For example, executing the following python file in file explorer opens an explorer window at c:/temp
:
import subprocess
subprocess.Popen(["explorer", "C:/Temp"])