tywrap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +263 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/index.d.ts +41 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +234 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/analyzer.d.ts +63 -0
- package/dist/core/analyzer.d.ts.map +1 -0
- package/dist/core/analyzer.js +676 -0
- package/dist/core/analyzer.js.map +1 -0
- package/dist/core/discovery.d.ts +85 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +344 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/generator.d.ts +37 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +368 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/mapper.d.ts +40 -0
- package/dist/core/mapper.d.ts.map +1 -0
- package/dist/core/mapper.js +401 -0
- package/dist/core/mapper.js.map +1 -0
- package/dist/core/validation.d.ts +79 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +486 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/base.d.ts +12 -0
- package/dist/runtime/base.d.ts.map +1 -0
- package/dist/runtime/base.js +6 -0
- package/dist/runtime/base.js.map +1 -0
- package/dist/runtime/errors.d.ts +17 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +21 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/http.d.ts +22 -0
- package/dist/runtime/http.d.ts.map +1 -0
- package/dist/runtime/http.js +79 -0
- package/dist/runtime/http.js.map +1 -0
- package/dist/runtime/index.d.ts +8 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +17 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/node.d.ts +48 -0
- package/dist/runtime/node.d.ts.map +1 -0
- package/dist/runtime/node.js +328 -0
- package/dist/runtime/node.js.map +1 -0
- package/dist/runtime/optimized-node.d.ts +131 -0
- package/dist/runtime/optimized-node.d.ts.map +1 -0
- package/dist/runtime/optimized-node.js +622 -0
- package/dist/runtime/optimized-node.js.map +1 -0
- package/dist/runtime/protocol.d.ts +3 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +3 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/runtime/pyodide.d.ts +27 -0
- package/dist/runtime/pyodide.d.ts.map +1 -0
- package/dist/runtime/pyodide.js +191 -0
- package/dist/runtime/pyodide.js.map +1 -0
- package/dist/tools/python_suite.js +173 -0
- package/dist/tools/python_suite.js.map +1 -0
- package/dist/types/index.d.ts +299 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/tywrap.d.ts +29 -0
- package/dist/tywrap.d.ts.map +1 -0
- package/dist/tywrap.js +589 -0
- package/dist/tywrap.js.map +1 -0
- package/dist/utils/bundle-optimizer.d.ts +182 -0
- package/dist/utils/bundle-optimizer.d.ts.map +1 -0
- package/dist/utils/bundle-optimizer.js +680 -0
- package/dist/utils/bundle-optimizer.js.map +1 -0
- package/dist/utils/cache.d.ts +149 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +492 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/codec.d.ts +94 -0
- package/dist/utils/codec.d.ts.map +1 -0
- package/dist/utils/codec.js +150 -0
- package/dist/utils/codec.js.map +1 -0
- package/dist/utils/logger.d.ts +42 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +140 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/memory-profiler.d.ts +123 -0
- package/dist/utils/memory-profiler.d.ts.map +1 -0
- package/dist/utils/memory-profiler.js +426 -0
- package/dist/utils/memory-profiler.js.map +1 -0
- package/dist/utils/parallel-processor.d.ts +146 -0
- package/dist/utils/parallel-processor.d.ts.map +1 -0
- package/dist/utils/parallel-processor.js +636 -0
- package/dist/utils/parallel-processor.js.map +1 -0
- package/dist/utils/python.d.ts +8 -0
- package/dist/utils/python.d.ts.map +1 -0
- package/dist/utils/python.js +56 -0
- package/dist/utils/python.js.map +1 -0
- package/dist/utils/runtime.d.ts +153 -0
- package/dist/utils/runtime.d.ts.map +1 -0
- package/dist/utils/runtime.js +519 -0
- package/dist/utils/runtime.js.map +1 -0
- package/package.json +136 -0
- package/runtime/python_bridge.py +476 -0
- package/src/cli.ts +331 -0
- package/src/config/index.ts +289 -0
- package/src/core/analyzer.ts +788 -0
- package/src/core/discovery.ts +415 -0
- package/src/core/generator.ts +447 -0
- package/src/core/mapper.ts +504 -0
- package/src/core/validation.ts +693 -0
- package/src/index.ts +83 -0
- package/src/runtime/base.ts +32 -0
- package/src/runtime/errors.ts +19 -0
- package/src/runtime/http.ts +132 -0
- package/src/runtime/index.ts +24 -0
- package/src/runtime/node.ts +431 -0
- package/src/runtime/optimized-node.ts +838 -0
- package/src/runtime/protocol.ts +2 -0
- package/src/runtime/pyodide.ts +228 -0
- package/src/types/global.d.ts +60 -0
- package/src/types/index.ts +410 -0
- package/src/tywrap.ts +669 -0
- package/src/utils/bundle-optimizer.ts +876 -0
- package/src/utils/cache.ts +634 -0
- package/src/utils/codec.ts +275 -0
- package/src/utils/logger.ts +201 -0
- package/src/utils/memory-profiler.ts +582 -0
- package/src/utils/parallel-processor.ts +879 -0
- package/src/utils/python.ts +79 -0
- package/src/utils/runtime.ts +616 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import importlib
|
|
5
|
+
import os
|
|
6
|
+
import traceback
|
|
7
|
+
import base64
|
|
8
|
+
import datetime as dt
|
|
9
|
+
import decimal
|
|
10
|
+
import uuid
|
|
11
|
+
from pathlib import Path, PurePath
|
|
12
|
+
|
|
13
|
+
instances = {}
|
|
14
|
+
|
|
15
|
+
FALLBACK_JSON = os.environ.get('TYWRAP_CODEC_FALLBACK', '').lower() == 'json'
|
|
16
|
+
PROTOCOL = 'tywrap/1'
|
|
17
|
+
PROTOCOL_VERSION = 1
|
|
18
|
+
BRIDGE_NAME = 'python-subprocess'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProtocolError(Exception):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def arrow_available():
|
|
26
|
+
try:
|
|
27
|
+
import pyarrow # noqa: F401
|
|
28
|
+
except Exception:
|
|
29
|
+
return False
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_numpy_array(obj):
|
|
34
|
+
try:
|
|
35
|
+
import numpy as np # noqa: F401
|
|
36
|
+
except Exception:
|
|
37
|
+
return False
|
|
38
|
+
return isinstance(obj, np.ndarray)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_pandas_dataframe(obj):
|
|
42
|
+
try:
|
|
43
|
+
import pandas as pd # noqa: F401
|
|
44
|
+
except Exception:
|
|
45
|
+
return False
|
|
46
|
+
return isinstance(obj, pd.DataFrame)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def is_pandas_series(obj):
|
|
50
|
+
try:
|
|
51
|
+
import pandas as pd # noqa: F401
|
|
52
|
+
except Exception:
|
|
53
|
+
return False
|
|
54
|
+
return isinstance(obj, pd.Series)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_scipy_sparse(obj):
|
|
58
|
+
try:
|
|
59
|
+
import scipy.sparse as sp # noqa: F401
|
|
60
|
+
except Exception:
|
|
61
|
+
return False
|
|
62
|
+
try:
|
|
63
|
+
return sp.issparse(obj)
|
|
64
|
+
except Exception:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_torch_tensor(obj):
|
|
69
|
+
try:
|
|
70
|
+
import torch # noqa: F401
|
|
71
|
+
except Exception:
|
|
72
|
+
return False
|
|
73
|
+
try:
|
|
74
|
+
return torch.is_tensor(obj)
|
|
75
|
+
except Exception:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def is_sklearn_estimator(obj):
|
|
80
|
+
try:
|
|
81
|
+
from sklearn.base import BaseEstimator # noqa: F401
|
|
82
|
+
except Exception:
|
|
83
|
+
return False
|
|
84
|
+
return isinstance(obj, BaseEstimator)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def serialize_ndarray(obj):
|
|
88
|
+
try:
|
|
89
|
+
import pyarrow as pa # type: ignore
|
|
90
|
+
except Exception as exc:
|
|
91
|
+
if FALLBACK_JSON:
|
|
92
|
+
return serialize_ndarray_json(obj)
|
|
93
|
+
raise RuntimeError(
|
|
94
|
+
'Arrow encoding unavailable for ndarray; install pyarrow or set TYWRAP_CODEC_FALLBACK=json to enable JSON fallback'
|
|
95
|
+
) from exc
|
|
96
|
+
try:
|
|
97
|
+
arr = pa.array(obj)
|
|
98
|
+
sink = pa.BufferOutputStream()
|
|
99
|
+
with pa.ipc.new_stream(sink, arr.type) as writer:
|
|
100
|
+
writer.write(arr)
|
|
101
|
+
buf = sink.getvalue()
|
|
102
|
+
b64 = base64.b64encode(buf.to_pybytes()).decode('ascii')
|
|
103
|
+
return {
|
|
104
|
+
'__tywrap__': 'ndarray',
|
|
105
|
+
'encoding': 'arrow',
|
|
106
|
+
'b64': b64,
|
|
107
|
+
'shape': getattr(obj, 'shape', None),
|
|
108
|
+
}
|
|
109
|
+
except Exception as exc:
|
|
110
|
+
if FALLBACK_JSON:
|
|
111
|
+
return serialize_ndarray_json(obj)
|
|
112
|
+
raise RuntimeError('Arrow encoding failed for ndarray') from exc
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def serialize_ndarray_json(obj):
|
|
116
|
+
try:
|
|
117
|
+
data = obj.tolist()
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
raise RuntimeError('JSON fallback failed for ndarray') from exc
|
|
120
|
+
return {
|
|
121
|
+
'__tywrap__': 'ndarray',
|
|
122
|
+
'encoding': 'json',
|
|
123
|
+
'data': data,
|
|
124
|
+
'shape': getattr(obj, 'shape', None),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def serialize_dataframe(obj):
|
|
129
|
+
try:
|
|
130
|
+
import pyarrow as pa # type: ignore
|
|
131
|
+
import pyarrow.feather as feather # type: ignore
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
if FALLBACK_JSON:
|
|
134
|
+
return serialize_dataframe_json(obj)
|
|
135
|
+
raise RuntimeError(
|
|
136
|
+
'Arrow encoding unavailable for pandas.DataFrame; install pyarrow or set TYWRAP_CODEC_FALLBACK=json to enable JSON fallback'
|
|
137
|
+
) from exc
|
|
138
|
+
try:
|
|
139
|
+
table = pa.Table.from_pandas(obj) # type: ignore
|
|
140
|
+
sink = pa.BufferOutputStream()
|
|
141
|
+
feather.write_feather(table, sink)
|
|
142
|
+
buf = sink.getvalue()
|
|
143
|
+
b64 = base64.b64encode(buf.to_pybytes()).decode('ascii')
|
|
144
|
+
return {
|
|
145
|
+
'__tywrap__': 'dataframe',
|
|
146
|
+
'encoding': 'arrow',
|
|
147
|
+
'b64': b64,
|
|
148
|
+
}
|
|
149
|
+
except Exception as exc:
|
|
150
|
+
if FALLBACK_JSON:
|
|
151
|
+
return serialize_dataframe_json(obj)
|
|
152
|
+
raise RuntimeError('Arrow encoding failed for pandas.DataFrame') from exc
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def serialize_dataframe_json(obj):
|
|
156
|
+
try:
|
|
157
|
+
data = obj.to_dict(orient='records')
|
|
158
|
+
except Exception as exc:
|
|
159
|
+
raise RuntimeError('JSON fallback failed for pandas.DataFrame') from exc
|
|
160
|
+
return {
|
|
161
|
+
'__tywrap__': 'dataframe',
|
|
162
|
+
'encoding': 'json',
|
|
163
|
+
'data': data,
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def serialize_series(obj):
|
|
168
|
+
try:
|
|
169
|
+
import pyarrow as pa # type: ignore
|
|
170
|
+
except Exception as exc:
|
|
171
|
+
if FALLBACK_JSON:
|
|
172
|
+
return serialize_series_json(obj)
|
|
173
|
+
raise RuntimeError(
|
|
174
|
+
'Arrow encoding unavailable for pandas.Series; install pyarrow or set TYWRAP_CODEC_FALLBACK=json to enable JSON fallback'
|
|
175
|
+
) from exc
|
|
176
|
+
try:
|
|
177
|
+
arr = pa.Array.from_pandas(obj) # type: ignore
|
|
178
|
+
sink = pa.BufferOutputStream()
|
|
179
|
+
with pa.ipc.new_stream(sink, arr.type) as writer:
|
|
180
|
+
writer.write(arr)
|
|
181
|
+
buf = sink.getvalue()
|
|
182
|
+
b64 = base64.b64encode(buf.to_pybytes()).decode('ascii')
|
|
183
|
+
return {
|
|
184
|
+
'__tywrap__': 'series',
|
|
185
|
+
'encoding': 'arrow',
|
|
186
|
+
'b64': b64,
|
|
187
|
+
'name': getattr(obj, 'name', None),
|
|
188
|
+
}
|
|
189
|
+
except Exception as exc:
|
|
190
|
+
if FALLBACK_JSON:
|
|
191
|
+
return serialize_series_json(obj)
|
|
192
|
+
raise RuntimeError('Arrow encoding failed for pandas.Series') from exc
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def serialize_series_json(obj):
|
|
196
|
+
try:
|
|
197
|
+
data = obj.to_list() # type: ignore
|
|
198
|
+
except Exception:
|
|
199
|
+
try:
|
|
200
|
+
data = obj.to_dict() # type: ignore
|
|
201
|
+
except Exception as exc:
|
|
202
|
+
raise RuntimeError('JSON fallback failed for pandas.Series') from exc
|
|
203
|
+
return {
|
|
204
|
+
'__tywrap__': 'series',
|
|
205
|
+
'encoding': 'json',
|
|
206
|
+
'data': data,
|
|
207
|
+
'name': getattr(obj, 'name', None),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def serialize_sparse_matrix(obj):
|
|
212
|
+
try:
|
|
213
|
+
fmt = obj.getformat()
|
|
214
|
+
except Exception as exc:
|
|
215
|
+
raise RuntimeError('Failed to inspect scipy sparse matrix format') from exc
|
|
216
|
+
|
|
217
|
+
if fmt not in ('csr', 'csc', 'coo'):
|
|
218
|
+
raise RuntimeError(f'Unsupported scipy sparse format: {fmt}')
|
|
219
|
+
|
|
220
|
+
dtype = None
|
|
221
|
+
try:
|
|
222
|
+
dtype = str(obj.dtype)
|
|
223
|
+
except Exception:
|
|
224
|
+
dtype = None
|
|
225
|
+
if getattr(obj.dtype, 'kind', None) == 'c':
|
|
226
|
+
raise RuntimeError('Complex sparse matrices are not supported by JSON codec')
|
|
227
|
+
|
|
228
|
+
if fmt in ('csr', 'csc'):
|
|
229
|
+
data = obj.data.tolist()
|
|
230
|
+
indices = obj.indices.tolist()
|
|
231
|
+
indptr = obj.indptr.tolist()
|
|
232
|
+
return {
|
|
233
|
+
'__tywrap__': 'scipy.sparse',
|
|
234
|
+
'encoding': 'json',
|
|
235
|
+
'format': fmt,
|
|
236
|
+
'shape': list(obj.shape),
|
|
237
|
+
'data': data,
|
|
238
|
+
'indices': indices,
|
|
239
|
+
'indptr': indptr,
|
|
240
|
+
'dtype': dtype,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# coo
|
|
244
|
+
data = obj.data.tolist()
|
|
245
|
+
row = obj.row.tolist()
|
|
246
|
+
col = obj.col.tolist()
|
|
247
|
+
return {
|
|
248
|
+
'__tywrap__': 'scipy.sparse',
|
|
249
|
+
'encoding': 'json',
|
|
250
|
+
'format': fmt,
|
|
251
|
+
'shape': list(obj.shape),
|
|
252
|
+
'data': data,
|
|
253
|
+
'row': row,
|
|
254
|
+
'col': col,
|
|
255
|
+
'dtype': dtype,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def serialize_torch_tensor(obj):
|
|
260
|
+
allow_copy = os.environ.get('TYWRAP_TORCH_ALLOW_COPY', '').lower() in ('1', 'true', 'yes')
|
|
261
|
+
tensor = obj.detach()
|
|
262
|
+
if getattr(tensor, 'device', None) is not None and tensor.device.type != 'cpu':
|
|
263
|
+
if not allow_copy:
|
|
264
|
+
raise RuntimeError(
|
|
265
|
+
'Torch tensor is on a non-CPU device; set TYWRAP_TORCH_ALLOW_COPY=1 to allow CPU transfer'
|
|
266
|
+
)
|
|
267
|
+
tensor = tensor.to('cpu')
|
|
268
|
+
if hasattr(tensor, 'is_contiguous') and not tensor.is_contiguous():
|
|
269
|
+
if not allow_copy:
|
|
270
|
+
raise RuntimeError(
|
|
271
|
+
'Torch tensor is not contiguous; set TYWRAP_TORCH_ALLOW_COPY=1 to allow contiguous copy'
|
|
272
|
+
)
|
|
273
|
+
tensor = tensor.contiguous()
|
|
274
|
+
try:
|
|
275
|
+
arr = tensor.numpy()
|
|
276
|
+
except Exception as exc:
|
|
277
|
+
raise RuntimeError('Failed to convert torch.Tensor to numpy') from exc
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
'__tywrap__': 'torch.tensor',
|
|
281
|
+
'encoding': 'ndarray',
|
|
282
|
+
'value': serialize_ndarray(arr),
|
|
283
|
+
'shape': list(tensor.shape),
|
|
284
|
+
'dtype': str(tensor.dtype),
|
|
285
|
+
'device': str(tensor.device),
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def serialize_sklearn_estimator(obj):
|
|
290
|
+
try:
|
|
291
|
+
import sklearn # noqa: F401
|
|
292
|
+
except Exception as exc:
|
|
293
|
+
raise RuntimeError('scikit-learn is not available') from exc
|
|
294
|
+
|
|
295
|
+
params = obj.get_params(deep=False)
|
|
296
|
+
try:
|
|
297
|
+
json.dumps(params)
|
|
298
|
+
except Exception as exc:
|
|
299
|
+
raise RuntimeError(
|
|
300
|
+
'scikit-learn estimator params are not JSON-serializable; avoid returning estimators or sanitize params'
|
|
301
|
+
) from exc
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
'__tywrap__': 'sklearn.estimator',
|
|
305
|
+
'encoding': 'json',
|
|
306
|
+
'className': obj.__class__.__name__,
|
|
307
|
+
'module': obj.__class__.__module__,
|
|
308
|
+
'version': getattr(sklearn, '__version__', None),
|
|
309
|
+
'params': params,
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
def serialize(obj):
|
|
313
|
+
if is_numpy_array(obj):
|
|
314
|
+
return serialize_ndarray(obj)
|
|
315
|
+
if is_pandas_dataframe(obj):
|
|
316
|
+
return serialize_dataframe(obj)
|
|
317
|
+
if is_pandas_series(obj):
|
|
318
|
+
return serialize_series(obj)
|
|
319
|
+
if is_scipy_sparse(obj):
|
|
320
|
+
return serialize_sparse_matrix(obj)
|
|
321
|
+
if is_torch_tensor(obj):
|
|
322
|
+
return serialize_torch_tensor(obj)
|
|
323
|
+
if is_sklearn_estimator(obj):
|
|
324
|
+
return serialize_sklearn_estimator(obj)
|
|
325
|
+
stdlib_value = serialize_stdlib(obj)
|
|
326
|
+
if stdlib_value is not None:
|
|
327
|
+
return stdlib_value
|
|
328
|
+
return obj
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def serialize_stdlib(obj):
|
|
332
|
+
if isinstance(obj, dt.datetime):
|
|
333
|
+
return obj.isoformat()
|
|
334
|
+
if isinstance(obj, dt.date):
|
|
335
|
+
return obj.isoformat()
|
|
336
|
+
if isinstance(obj, dt.time):
|
|
337
|
+
return obj.isoformat()
|
|
338
|
+
if isinstance(obj, dt.timedelta):
|
|
339
|
+
return obj.total_seconds()
|
|
340
|
+
if isinstance(obj, decimal.Decimal):
|
|
341
|
+
return str(obj)
|
|
342
|
+
if isinstance(obj, uuid.UUID):
|
|
343
|
+
return str(obj)
|
|
344
|
+
if isinstance(obj, (Path, PurePath)):
|
|
345
|
+
return str(obj)
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def handle_call(params):
|
|
350
|
+
module_name = params.get('module')
|
|
351
|
+
function_name = params.get('functionName')
|
|
352
|
+
args = params.get('args') or []
|
|
353
|
+
kwargs = params.get('kwargs') or {}
|
|
354
|
+
mod = importlib.import_module(module_name)
|
|
355
|
+
func = getattr(mod, function_name)
|
|
356
|
+
res = func(*args, **kwargs)
|
|
357
|
+
return serialize(res)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def handle_instantiate(params):
|
|
361
|
+
module_name = params.get('module')
|
|
362
|
+
class_name = params.get('className')
|
|
363
|
+
args = params.get('args') or []
|
|
364
|
+
kwargs = params.get('kwargs') or {}
|
|
365
|
+
mod = importlib.import_module(module_name)
|
|
366
|
+
cls = getattr(mod, class_name)
|
|
367
|
+
obj = cls(*args, **kwargs)
|
|
368
|
+
handle_id = str(id(obj))
|
|
369
|
+
instances[handle_id] = obj
|
|
370
|
+
return handle_id
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def handle_call_method(params):
|
|
374
|
+
handle_id = params.get('handle')
|
|
375
|
+
method_name = params.get('methodName')
|
|
376
|
+
args = params.get('args') or []
|
|
377
|
+
kwargs = params.get('kwargs') or {}
|
|
378
|
+
if handle_id not in instances:
|
|
379
|
+
raise KeyError(f'Unknown handle: {handle_id}')
|
|
380
|
+
obj = instances[handle_id]
|
|
381
|
+
func = getattr(obj, method_name)
|
|
382
|
+
res = func(*args, **kwargs)
|
|
383
|
+
return serialize(res)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def handle_dispose_instance(params):
|
|
387
|
+
handle_id = params.get('handle')
|
|
388
|
+
if handle_id not in instances:
|
|
389
|
+
raise KeyError(f'Unknown handle: {handle_id}')
|
|
390
|
+
del instances[handle_id]
|
|
391
|
+
return True
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def handle_meta():
|
|
395
|
+
return {
|
|
396
|
+
'protocol': PROTOCOL,
|
|
397
|
+
'protocolVersion': PROTOCOL_VERSION,
|
|
398
|
+
'bridge': BRIDGE_NAME,
|
|
399
|
+
'pythonVersion': sys.version.split()[0],
|
|
400
|
+
'pid': os.getpid(),
|
|
401
|
+
'codecFallback': 'json' if FALLBACK_JSON else 'none',
|
|
402
|
+
'arrowAvailable': arrow_available(),
|
|
403
|
+
'instances': len(instances),
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def require_protocol(msg):
|
|
408
|
+
proto = msg.get('protocol')
|
|
409
|
+
if proto != PROTOCOL:
|
|
410
|
+
raise ProtocolError(f'Invalid protocol: {proto}')
|
|
411
|
+
mid = msg.get('id')
|
|
412
|
+
if not isinstance(mid, int):
|
|
413
|
+
raise ProtocolError(f'Invalid request id: {mid}')
|
|
414
|
+
return mid
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def main():
|
|
418
|
+
for line in sys.stdin:
|
|
419
|
+
line = line.strip()
|
|
420
|
+
if not line:
|
|
421
|
+
continue
|
|
422
|
+
mid = None
|
|
423
|
+
out = None
|
|
424
|
+
try:
|
|
425
|
+
msg = json.loads(line)
|
|
426
|
+
mid = require_protocol(msg)
|
|
427
|
+
method = msg.get('method')
|
|
428
|
+
if not isinstance(method, str):
|
|
429
|
+
raise ProtocolError('Missing method')
|
|
430
|
+
params = msg.get('params') or {}
|
|
431
|
+
if not isinstance(params, dict):
|
|
432
|
+
raise ProtocolError('Invalid params')
|
|
433
|
+
try:
|
|
434
|
+
if method == 'call':
|
|
435
|
+
result = handle_call(params)
|
|
436
|
+
elif method == 'instantiate':
|
|
437
|
+
result = handle_instantiate(params)
|
|
438
|
+
elif method == 'call_method':
|
|
439
|
+
result = handle_call_method(params)
|
|
440
|
+
elif method == 'dispose_instance':
|
|
441
|
+
result = handle_dispose_instance(params)
|
|
442
|
+
elif method == 'meta':
|
|
443
|
+
result = handle_meta()
|
|
444
|
+
else:
|
|
445
|
+
raise ValueError('Unknown method')
|
|
446
|
+
out = { 'id': mid, 'protocol': PROTOCOL, 'result': result }
|
|
447
|
+
except Exception as e:
|
|
448
|
+
out = {
|
|
449
|
+
'id': mid,
|
|
450
|
+
'protocol': PROTOCOL,
|
|
451
|
+
'error': { 'type': type(e).__name__, 'message': str(e), 'traceback': traceback.format_exc() }
|
|
452
|
+
}
|
|
453
|
+
except Exception as e:
|
|
454
|
+
out = {
|
|
455
|
+
'id': mid if mid is not None else -1,
|
|
456
|
+
'protocol': PROTOCOL,
|
|
457
|
+
'error': { 'type': type(e).__name__, 'message': str(e) }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
sys.stdout.write(json.dumps(out) + '\n')
|
|
462
|
+
except Exception as e:
|
|
463
|
+
err_out = {
|
|
464
|
+
'id': mid if mid is not None else -1,
|
|
465
|
+
'protocol': PROTOCOL,
|
|
466
|
+
'error': {
|
|
467
|
+
'type': type(e).__name__,
|
|
468
|
+
'message': f'Failed to serialize response: {e}'
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
sys.stdout.write(json.dumps(err_out) + '\n')
|
|
472
|
+
sys.stdout.flush()
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
if __name__ == '__main__':
|
|
476
|
+
main()
|