Skip to content

Commit b91ef27

Browse files
author
DB
committed
Run on Python2 and 3; add debugging to file; fix double-json decoding issue
1 parent 3728866 commit b91ef27

File tree

2 files changed

+56
-20
lines changed

2 files changed

+56
-20
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,12 @@ The important parts are the **`name`**, which uniquely identifies the NM host, t
183183

184184
This NM manifest has to be copied or symlinked [to the correct location for your operating system](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests), under Linux a suitable location is `~/.mozilla/native-messaging-hosts/<name>.json`, in our case thus `~/.mozilla/native-messaging-hosts/runwith.json`.
185185

186-
A Python NM program that works the way the addon expects (**`runwith.py`**) [is included in the repo](https://github.com/waldner/Firefox-RunWith/blob/master/runwith.py), so you should copy it somewhere and update its path in the NM manifest.
186+
A Python NM program that works the way the addon expects (**`runwith.py`**) [is included in the repo](https://github.com/waldner/Firefox-RunWith/blob/master/runwith.py), so you should copy it somewhere and update its path in the NM manifest. It should work with Python 2 and Python 3.
187187

188188
**`runwith.py`** speaks the NM protocol, it expects to receive on stdin a JSON array with the command to run and its arguments, runs the command according to the user's shell/wait preferences, then writes back (to the extension) a brief summary of the execution (in case you're interested, it can be seen in the browser console, which can be opened with CTRL+SHIFT+J, along with other debugging messages output by the [**`background.js`**](https://github.com/waldner/Firefox-RunWith/blob/master/addon/background.js) script).
189189

190+
Inside `runwith.py`, you can set the **`enable_debug`** variable to `True` to dump the various incoming and outgoing messages it processes to a file (by default, `/tmp/runwith_debug.log`, see the `debug_file` variable).
191+
190192
#### What do "`shell`" and "`wait`" do exactly?
191193

192194
Let's assume you have a command like the following for an action in your configuration:

runwith.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,70 @@
1-
#!/usr/bin/python2
1+
#!/usr/bin/python
2+
23
# Note that running python with the `-u` flag is required on Windows,
34
# in order to ensure that stdin and stdout are opened in binary, rather
45
# than text, mode.
56

67
import sys, json, struct, os
78
import subprocess
89

10+
def debug(msg):
11+
if enable_debug:
12+
with open(debug_file, 'a') as outfile:
13+
outfile.write(msg)
14+
outfile.write("\n")
15+
916
# Read a message from stdin and decode it.
1017
def getMessage():
11-
rawLength = sys.stdin.read(4)
18+
rawLength = our_stdin.read(4)
1219
if len(rawLength) == 0:
1320
sys.exit(0)
1421
messageLength = struct.unpack('@I', rawLength)[0]
15-
message = sys.stdin.read(messageLength)
22+
debug("getMessage: messageLength is %s" % messageLength)
23+
message = our_stdin.read(messageLength).decode()
24+
debug("getMessage: message is %s" % message)
1625
return json.loads(message)
1726

1827
# Encode a message for transmission, given its content.
1928
def encodeMessage(messageContent):
29+
debug("encodeMessage: messageContent is %s" % messageContent)
2030
encodedContent = json.dumps(messageContent)
31+
debug("encodeMessage: encodedContent is %s\n" % encodedContent)
2132
encodedLength = struct.pack('@I', len(encodedContent))
22-
return {'length': encodedLength, 'content': encodedContent}
33+
debug("encodeMessage: encodedLength is %s" % encodedLength)
34+
return {'length': encodedLength, 'content': encodedContent.encode()}
2335

2436
# Send an encoded message to stdout.
2537
def sendMessage(encodedMessage):
26-
sys.stdout.write(encodedMessage['length'])
27-
sys.stdout.write(encodedMessage['content'])
28-
sys.stdout.flush()
2938

39+
debug("sendMessage: encodedMessage is %s" % encodedMessage)
40+
debug("sendMessage: encodedMessage['length'] is %s" % encodedMessage['length'])
41+
debug("sendMessage: encodedMessage['content'] is %s" % encodedMessage['content'])
42+
43+
our_stdout.write(encodedMessage['length'])
44+
our_stdout.write(encodedMessage['content'])
45+
our_stdout.flush()
46+
47+
# determine Python version
48+
python_version = sys.version_info[0] # should be 2 or 3
49+
50+
if python_version == 2:
51+
our_stdin = sys.stdin
52+
our_stdout = sys.stdout
53+
else:
54+
our_stdin = sys.stdin.buffer
55+
our_stdout = sys.stdout.buffer
3056

31-
receivedMessage = getMessage()
3257

33-
msg = json.loads(receivedMessage)
58+
# set this to true to dump things to the debug_file
59+
enable_debug = False
60+
debug_file = "/tmp/runwith_debug.log"
3461

35-
cmd = msg['cmd']
36-
shell = msg['shell']
37-
wait = msg['wait']
62+
receivedMessage = getMessage()
63+
debug("main: receivedMessage is %s" % receivedMessage)
64+
65+
cmd = receivedMessage['cmd']
66+
shell = receivedMessage['shell']
67+
wait = receivedMessage['wait']
3868

3969
use_shell = False
4070
command = cmd
@@ -43,21 +73,25 @@ def sendMessage(encodedMessage):
4373
command = ' '.join(cmd)
4474

4575
devnull = open(os.devnull, 'w')
46-
stdout = devnull
47-
stderr = devnull
76+
child_stdout = devnull
77+
child_stderr = devnull
4878
close_fds = True
4979
if wait:
50-
stdout = subprocess.PIPE
51-
stderr = subprocess.PIPE
80+
child_stdout = subprocess.PIPE
81+
child_stderr = subprocess.PIPE
5282
close_fds = False
5383

54-
proc = subprocess.Popen(command, stdout = stdout, stderr = stderr, shell = use_shell, close_fds = close_fds)
84+
proc = subprocess.Popen(command, stdout = child_stdout, stderr = child_stderr, shell = use_shell, close_fds = close_fds)
85+
86+
msg = { 'pid': proc.pid, 'shell': use_shell, 'wait': wait }
5587

5688
if wait:
5789
stdout, stderr = proc.communicate()
5890
exit_code = proc.returncode
59-
msg = "command ran, exit status: %s, stderr: %s" % (exit_code, stderr)
91+
msg['exit-status'] = exit_code
92+
msg['stderr'] = stderr.decode()
6093
else:
61-
msg = "async process launched, PID: %s" % proc.pid
94+
msg['exit-status'] = 'N/A'
95+
msg['stderr'] = 'N/A'
6296

6397
sendMessage(encodeMessage(msg))

0 commit comments

Comments
 (0)