Native Messaging: Python programme executes all lines but does not run any 'subprocess.Popen()' lines

I have simple Firefox add-on that, I am using it to test and discover the capabilities of Native Messaging, something I recently came across.

The add-on side of things works as I expect it, nothing out of order there, I am struggling with the Python side of the add-on.

The add-on works like this:

  1. On the browser, when the page HTML basics is visited
  2. The add-on will send the message “ping” to a Python script, on the local system, via stdin
  3. The python script should reply back to the browser the message it received via stdin and run a process via subprocess.Popen()

Sure it enough, in all my tests, on the browser console, I can see the Python programme sending back a reply like THIS. But the line subprocess.Popen(["explorer", "C:/Temp"]) is never executed at all. No matter where I place it in the Python script.

If I create a separate Python script with just the following code and run it by double clicking the file in explorer, it works. A explorer window is created:

import subprocess
subprocess.Popen(["explorer", "C:/Temp"])

Of course I am looking to do more than just open a explorer window, its just a simple example. The point is for some reason, my Python programme is stuck either reading at stdin or somewhere else.

I tried restructuring the Python code to something simple and tried “closing” the stdin stream to see if that will help it carry on with the execution of the remaining lines:

import sys
import json
import struct
import subprocess

rawLength = sys.stdin.buffer.read(4)
if len(rawLength) == 0:
	sys.exit(0)
messageLength = struct.unpack('@I', rawLength)[0]
message = sys.stdin.buffer.read(messageLength).decode('utf-8')
sys.stdin.buffer.flush()   						#Try closing the stdin buffer
sys.stdin.buffer.close()   						#Try closing the stdin buffer

subprocess.Popen(["explorer", "C:/Temp"])    #Again not executed

Same issue persists, the last line is again not executed. I am new to Python, JavaScript and add-on development. I asked around for any debugging tools for such a novel, edge use case but sadly I have not turned up with answers. The python programme does not spawn its own console window so its hard to tell where execution is stuck at with something like print()

I did try the following in its own python script file:

import sys
import json
import struct
import subprocess

rawLength = sys.stdin.buffer.read(4)
print(rawLength)
subprocess.Popen(["explorer", "C:/Temp"])

It will spawn its own console window, the programme is stuck at rawLength = sys.stdin.buffer.read(4) and will remain there, even if I provide just a letter and press enter, it continues when I provide four letters, opening file explorer at c:/Temp.

Last time I asked around. I was told this might be what is happening and I looked for a way to stop the stdin stream reading or close it, which is what I tried to do with flush()/close() but it does not help.

Am I attempting to close the stdin stream the right way? If so am I succeeding? How does one know for sure? Is stdin even the culprit here?

I am out of ideas, any help would be greatly appreciated!


For completeness, my add-on is compromised of only two files, a manifest.json and a background.file.

Manifest.json file:

{
"name": "test",
"manifest_version": 2,
"version": "1.0",

"browser_action": {"default_icon": "icons/message.svg"},
"browser_specific_settings": {"gecko": {"id": "test@example.org","strict_min_version": "50.0"}},

"background": {"scripts": ["background.js"]},
"permissions": ["tabs","activeTab", "webRequest", "<all_urls>", "nativeMessaging"]
}

Background.json file:

browser.webRequest.onCompleted.addListener(sendNativeMsg, {urls:["https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics"]}); 
function onResponse(response) {console.log(`INCOMING MSG: ${response}`);}

function sendNativeMsg(activeTab) {
	let thisMsg = "ping"
	console.log(`OUTGOING MSG: "${thisMsg}"`);	

	let sending = browser.runtime.sendNativeMessage("test", thisMsg);
	sending.then(onResponse);
	}

And the source code for the Python script is the following, which I got from the Native Messaging, MDN page, linked above:

import sys
import json
import struct
import subprocess

# Read a message from stdin and decode it.
def getMessage():
	rawLength = sys.stdin.buffer.read(4)
	if len(rawLength) == 0:
		sys.exit(0)
	messageLength = struct.unpack('@I', rawLength)[0]
	message = sys.stdin.buffer.read(messageLength).decode('utf-8')
	return json.loads(message)

# Encode a message for transmission,
def encodeMessage(messageContent):
	encodedContent = json.dumps(messageContent, separators=(',', ':')).encode('utf-8')
	encodedLength = struct.pack('@I', len(encodedContent))
	return {'length': encodedLength, 'content': encodedContent}

# Send an encoded message to stdout
def sendMessage(encodedMessage):
	sys.stdout.buffer.write(encodedMessage['length'])
	sys.stdout.buffer.write(encodedMessage['content'])
	sys.stdout.buffer.flush()

while True:
	subprocess.Popen(["explorer", "C:/Temp"])       #This line is never executed. The lines after here are executed.
	receivedMessage = getMessage()
	if receivedMessage == "ping":
		sendMessage(encodeMessage('stdin was "' + receivedMessage + '", Task is done'))
1 Like

The subprocess module documentation says:

The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.

Real Python has a tutorial on the subprocess module:


But I can’t tell you if it’s any good, because I haven’t read it yet…
3 Likes

Sure enough run() was exactly what I needed. The part you quoted is also highlighted in the docs and I still went out of my way to overlook it, I truelly am my worst enemey.

That article you linked to was really informative on the library! it even explains the peculiar name subprocess. I can vouch for its quality.

Cheers for this mate. Really happy now