Skip to content

Commit 21217b0

Browse files
author
Ryan Munro
authored
Add more value serialization between JS & Python bridge (#26)
* Add more complete serde between JS and Python * Test Node 10 & 11, and Python 3.7
1 parent ddb33a4 commit 21217b0

4 files changed

Lines changed: 67 additions & 26 deletions

File tree

.travis.yml

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,16 @@ language: python
22
os:
33
- "linux"
44
env:
5+
- NODE_VERSION="11"
6+
- NODE_VERSION="10"
57
- NODE_VERSION="9"
68
- NODE_VERSION="8"
79
- NODE_VERSION="7.5"
8-
# - NODE_VERSION="7.3"
9-
# - NODE_VERSION="7.2"
10-
# - NODE_VERSION="7.1"
11-
# - NODE_VERSION="7.0"
1210
- NODE_VERSION="6.1"
13-
# - NODE_VERSION="6.0"
1411
- NODE_VERSION="5.11"
15-
# - NODE_VERSION="5.10"
16-
# - NODE_VERSION="5.9"
17-
# - NODE_VERSION="5.8"
18-
# - NODE_VERSION="5.7"
19-
# - NODE_VERSION="5.6"
20-
# - NODE_VERSION="5.5"
21-
# - NODE_VERSION="5.4"
22-
# - NODE_VERSION="5.3"
23-
# - NODE_VERSION="5.2"
24-
# - NODE_VERSION="5.1"
25-
# - NODE_VERSION="5.0"
2612
- NODE_VERSION="4.4"
27-
# - NODE_VERSION="4.3"
28-
# - NODE_VERSION="4.2"
29-
# - NODE_VERSION="4.1"
30-
# - NODE_VERSION="4.0"
3113
python:
32-
- "nightly"
14+
- "3.7-dev"
3315
- "3.6"
3416
- "3.5"
3517
- "3.4"

index.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ function pythonBridge(opts) {
2626
function wrapper() {
2727
self = self || wrapper;
2828
let code = json.apply(this, arguments);
29-
let on_message, on_close;
3029

3130
if (!(this && this.connected || self.connected)) {
3231
return Promise.reject(new PythonBridgeNotConnected());
@@ -40,7 +39,7 @@ function pythonBridge(opts) {
4039
function onMessage(data) {
4140
ps.removeListener('close', onClose);
4241
if (data && data.type && data.type === 'success') {
43-
resolve(data.value);
42+
resolve(eval(`(${data.value})`));
4443
} else if (data && data.type && data.type === 'exception') {
4544
reject(new PythonException(data.value));
4645
} else {
@@ -176,13 +175,45 @@ function dedent(code) {
176175

177176
function json(text_nodes) {
178177
let values = Array.prototype.slice.call(arguments, 1);
179-
return dedent(text_nodes.reduce((cur, acc, i) => cur + JSON.stringify(values[i - 1]) + acc));
178+
return dedent(text_nodes.reduce((cur, acc, i) => {
179+
return cur + serializePython(values[i - 1]) + acc;
180+
}));
181+
}
182+
183+
function serializePython(value) {
184+
if (value === null || typeof value === 'undefined') {
185+
return 'None';
186+
} else if (value === true) {
187+
return 'True';
188+
} else if (value === false) {
189+
return 'False';
190+
} else if (value === Infinity) {
191+
return "float('inf')";
192+
} else if (value === -Infinity) {
193+
return "float('-inf')";
194+
} else if (value instanceof Array) {
195+
return `[${value.map(serializePython).join(', ')}]`;
196+
} else if (typeof value === 'number') {
197+
if (isNaN(value)) {
198+
return "float('nan')";
199+
}
200+
return JSON.stringify(value);
201+
} else if (typeof value === 'string') {
202+
return JSON.stringify(value);
203+
} else if (value instanceof Map) {
204+
const props = Array.from(value.entries()).map(kv => `${serializePython(kv[0])}: ${serializePython(kv[1])}`);
205+
return `{${props.join(', ')}}`;
206+
} else {
207+
const props = Object.keys(value).map(k => `${serializePython(k)}: ${serializePython(value[k])}`);
208+
return `{${props.join(', ')}}`;
209+
}
180210
}
181211

182212
pythonBridge.pythonBridge = pythonBridge;
183213
pythonBridge.PythonException = PythonException;
184214
pythonBridge.PythonBridgeNotConnected = PythonBridgeNotConnected;
185215
pythonBridge.isPythonException = isPythonException;
186216
pythonBridge.json = json;
217+
pythonBridge.serializePython = serializePython;
187218

188219
module.exports = pythonBridge.pythonBridge = pythonBridge;

node_python_bridge.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import traceback
88
import platform
99
import struct
10+
import math
1011

1112
NODE_CHANNEL_FD = int(os.environ['NODE_CHANNEL_FD'])
1213
UNICODE_TYPE = unicode if sys.version_info[0] == 2 else str
@@ -65,6 +66,15 @@ def format_exception(t=None, e=None, tb=None):
6566
)
6667

6768

69+
class JavaScriptEncoder(json.JSONEncoder):
70+
def default(self, o):
71+
if math.isnan(o):
72+
return 'NaN'
73+
if math.isinf(o):
74+
return 'Infinity' if o > 0 else '-Infinity'
75+
return o.__dict__
76+
77+
6878
if __name__ == '__main__':
6979
writer = os.fdopen(NODE_CHANNEL_FD, 'wb')
7080
reader = os.fdopen(NODE_CHANNEL_FD, 'rb')
@@ -92,7 +102,7 @@ def format_exception(t=None, e=None, tb=None):
92102
response = dict(type='success')
93103
else:
94104
value = eval(_compile(data['code'], '<input>', 'eval'), _locals)
95-
response = dict(type='success', value=value)
105+
response = dict(type='success', value=json.dumps(value, separators=(',', ':'), cls=JavaScriptEncoder))
96106
except:
97107
t, e, tb = sys.exc_info()
98108
response = dict(type='exception', value=format_exception(t, e, tb))

test.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,24 @@ test('json interpolation', t => {
253253
`, 'def hello(a, b):\n return a + b\n');
254254
t.equal(pythonBridge.json`hello()`, 'hello()');
255255
t.equal(pythonBridge.json`hello(${'world'})`, 'hello("world")');
256-
t.equal(pythonBridge.json`hello(${'world'}, ${[1, 2, 3]})`, 'hello("world", [1,2,3])');
256+
t.equal(pythonBridge.json`hello(${'world'}, ${[1, 2, 3]})`, 'hello("world", [1, 2, 3])');
257+
t.equal(pythonBridge.json`hello(${new Map([[1, 2], [3, 4]])})`, 'hello({1: 2, 3: 4})');
258+
t.equal(pythonBridge.json`hello(${NaN}, ${Infinity}, ${-Infinity})`, "hello(float('nan'), float('inf'), float('-inf'))");
257259
t.end();
258260
});
261+
262+
test('bug #22 returning NaN or infinity does not work', t => {
263+
t.plan(1);
264+
const s = {a: NaN, b: Infinity, c: -Infinity};
265+
const python = pythonBridge();
266+
python`(lambda x: x)(${s})`.then(x => t.deepEqual(x, s));
267+
python.end();
268+
});
269+
270+
test('bug #24 support more than just numbers and strings', t => {
271+
t.plan(1);
272+
const s = {a: 'asdf', b: 1, c: true, d: [1, 2, null]};
273+
const python = pythonBridge();
274+
python`(lambda x: x)(${s})`.then(x => t.deepEqual(x, s));
275+
python.end();
276+
});

0 commit comments

Comments
 (0)