My simple python program does not execute the last line

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:

  1. A event occurs in the browser
  2. Firefox launches the Python program on the local system (achieved with Native Messaging)
  3. 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. :laughing:.

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"])